Skip to content

Commit 943ff93

Browse files
committed
Fixed Chrome compatibility issue and improved overall functionality
1 parent 910cc7a commit 943ff93

File tree

9 files changed

+185
-51
lines changed

9 files changed

+185
-51
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ https://yourdomain
9898
To stop and remove the containers, run:
9999

100100
```
101-
docker-compose rm -f
101+
docker-compose down
102102
```
103103

104104
## Under the hood

deploy/docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ services:
1717
command:
1818
- --fingerprint
1919
- --use-auth-secret
20-
- --static-auth-secret=<SECRET_KEY>
2120
- --no-multicast-peers
21+
- --realm=filesync.app
22+
- --static-auth-secret=<SECRET_KEY>
2223
ports:
2324
- "3478:3478/tcp"
2425
- "3478:3478/udp"

web/css/bootstrap.min.css

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
<title>FileSync.app | Send files securely in real-time.</title>
55
<meta name="description" content="Send files securely from one device to many in real-time.">
66
<meta charset="UTF-8">
7-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
87
<link rel="icon" type="image/png" href="assets/icon.png">
98
<link rel="apple-touch-icon" href="assets/icon.png">
109
<meta name="viewport" content="width=device-width, initial-scale=1.0">
11-
<link href='https://fonts.googleapis.com/css?family=Acme|Lato' rel='stylesheet'>
12-
<link rel="stylesheet" href="css/style.css">
10+
<link href="https://fonts.googleapis.com/css?family=Acme|Lato" rel="stylesheet">
11+
<link href="css/bootstrap.min.css" rel="stylesheet">
12+
<link href="css/style.css" rel="stylesheet">
1313
<script>
1414
if (window.localStorage.getItem('mode') == 'light') {
1515
document.documentElement.classList.remove("dark")
@@ -416,7 +416,7 @@ <h5 style="color: var(--color-h1); margin:0">
416416
</div>
417417
</div>
418418

419-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
419+
<script src="js/vendors/bootstrap.bundle.min.js" type="text/javascript"></script>
420420
<script src="js/vendors/jszip.min.js" type="text/javascript"></script>
421421
<script src="js/vendors/qrious.min.js" type="text/javascript"></script>
422422
<script src="js/vendors/crypto-js.min.js" type="text/javascript"></script>

web/js/modules/webrtc/file.js

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,41 +115,33 @@ export class File {
115115
}
116116

117117
async init(peer_id) {
118-
// Get short-lived TURN credentials
119-
let username, credential;
118+
// Get ICE servers
119+
let iceServers;
120120
try {
121-
({ username, credential } = await turn.getToken());
121+
iceServers = await turn.getServers();
122122
} catch (err) {
123123
console.log(err)
124124
dom.transfer_div.style.display = 'none'
125125
dom.connect_div.style.display = 'none'
126126
dom.error_div.style.display = 'block'
127-
dom.error_message.innerHTML = 'An error occurred getting the TURN credentials. Please try again later.'
127+
dom.error_message.innerHTML = 'An error occurred getting the ICE servers. Please try again later.'
128128
return
129129
}
130-
130+
131131
await new Promise(async (resolve) => {
132132
// Get UUID
133133
const uuid = await this._getUUID();
134134

135135
// Create a new Peer instance
136+
const isSecure = window.isSecureContext && window.location.hostname !== 'localhost';
136137
const peer = new Peer(uuid, {
137138
host: window.location.hostname,
138139
port: 9000,
139140
path: "/",
140-
secure: window.location.hostname !== 'localhost',
141+
secure: isSecure,
141142
config: {
142-
// iceTransportPolicy: "relay", // Force TURN-only
143-
iceServers: [
144-
{
145-
urls: `stun:${window.location.hostname}:3478`
146-
},
147-
{
148-
urls: `turn:${window.location.hostname}:3478`,
149-
username: username,
150-
credential: credential
151-
}
152-
]
143+
iceServers: iceServers,
144+
// iceTransportPolicy: "relay", // Force TURN
153145
}
154146
});
155147

web/js/modules/webrtc/turn.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
class Turn {
2-
_url = "/api/credentials";
3-
_expiration = null;
42
_username = null;
53
_credential = null;
4+
_expiration = null;
65

7-
_decodeJwt(token) {
8-
const payload = token.split(".")[1];
9-
const json = atob(payload);
10-
return JSON.parse(json);
6+
getServers = async () => {
7+
await this._getCredentials();
8+
return [
9+
{
10+
urls: `stun:${window.location.hostname}:3478`
11+
},
12+
{
13+
urls: `turn:${window.location.hostname}:3478`,
14+
username: this._username,
15+
credential: this._credential,
16+
}
17+
]
1118
}
1219

13-
getToken = async () => {
20+
_getCredentials = async () => {
1421
const now = Math.floor(Date.now() / 1000);
1522

1623
// Check if token is still valid
@@ -19,7 +26,7 @@ class Turn {
1926
}
2027

2128
// Fetch new token
22-
const response = await fetch(this._url, { method: "GET" });
29+
const response = await fetch("/api/credentials", { method: "GET" });
2330

2431
if (!response.ok) {
2532
throw new Error("An issue occurred while getting the token.");
@@ -36,13 +43,16 @@ class Turn {
3643
const payload = this._decodeJwt(token);
3744

3845
// Set cached values
39-
this._expiration = payload.exp - 10; // Subtract 10 seconds for safety
4046
this._username = payload.username;
4147
this._credential = payload.credential;
42-
43-
// Return the username and credential
44-
return { username: this._username, credential: this._credential };
48+
this._expiration = payload.exp - 10; // Subtract 10 seconds for safety
4549
};
50+
51+
_decodeJwt(token) {
52+
const payload = token.split(".")[1];
53+
const json = atob(payload);
54+
return JSON.parse(json);
55+
}
4656
}
4757

4858
export const turn = new Turn();

web/js/modules/webrtc/user.js

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,38 +46,30 @@ export class User {
4646
// Get UUID
4747
if (peer_id == null) peer_id = await this._getUUID();
4848

49-
// Get short-lived TURN credentials
50-
let username, credential;
49+
// Get ICE servers
50+
let iceServers;
5151
try {
52-
({ username, credential } = await turn.getToken());
52+
iceServers = await turn.getServers();
5353
} catch (err) {
5454
console.log(err)
5555
dom.transfer_div.style.display = 'none'
5656
dom.connect_div.style.display = 'none'
5757
dom.error_div.style.display = 'block'
58-
dom.error_message.innerHTML = 'An error occurred getting the TURN credentials. Please try again later.'
58+
dom.error_message.innerHTML = 'An error occurred getting the ICE servers. Please try again later.'
5959
return
6060
}
6161

6262
await new Promise((resolve) => {
6363
// Create a new Peer instance
64+
const isSecure = window.isSecureContext && window.location.hostname !== 'localhost';
6465
this._peer = new Peer(peer_id, {
6566
host: window.location.hostname,
6667
port: 9000,
6768
path: "/",
68-
secure: window.location.hostname !== 'localhost',
69+
secure: isSecure,
6970
config: {
70-
// iceTransportPolicy: "relay", // Force TURN-only
71-
iceServers: [
72-
{
73-
urls: `stun:${window.location.hostname}:3478`
74-
},
75-
{
76-
urls: `turn:${window.location.hostname}:3478`,
77-
username: username,
78-
credential: credential
79-
}
80-
]
71+
iceServers: iceServers,
72+
// iceTransportPolicy: "relay", // Force TURN
8173
}
8274
});
8375

web/js/vendors/bootstrap.bundle.min.js

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/test.html

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Test ICE Servers</title>
5+
<meta charset="utf-8" />
6+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
</head>
9+
10+
<body>
11+
<h1>Test ICE Servers</h1>
12+
<hr />
13+
<p id="ip"></p>
14+
<p id="stun">🔴 The STUN server is NOT reachable!</p>
15+
<p id="turn">🔴 The TURN server is NOT reachable!</p>
16+
<p id="err"></p>
17+
<hr />
18+
<script>
19+
const IP = document.getElementById('ip');
20+
const Stun = document.getElementById('stun');
21+
const Turn = document.getElementById('turn');
22+
const Err = document.getElementById('err');
23+
24+
document.addEventListener("DOMContentLoaded", main);
25+
26+
async function main() {
27+
const token = await _getToken()
28+
const payload = await _decodeJwt(token);
29+
const iceServers = [
30+
{
31+
urls: `stun:${window.location.hostname}:3478`
32+
},
33+
{
34+
urls: `turn:${window.location.hostname}:3478`,
35+
username: payload.username,
36+
credential: payload.credential
37+
}
38+
];
39+
40+
// Print iceServers config
41+
console.log("ICE Servers", iceServers)
42+
43+
// Create a new RTCPeerConnection using the STUN and TURN servers
44+
const pc = new RTCPeerConnection({ iceServers });
45+
46+
// Handle each ICE candidate as it is discovered.
47+
pc.onicecandidate = (e) => {
48+
// Ignore the final empty candidate (null means gathering finished)
49+
if (!e.candidate || e.candidate.candidate === '') return;
50+
51+
// Show candidate
52+
console.log(e.candidate.candidate);
53+
54+
// Detect and indicate if a STUN candidate is found and gather the Public IP address
55+
if (e.candidate.type == 'srflx' || e.candidate.candidate.includes('srflx')) {
56+
let ip = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
57+
let address = e.candidate.address
58+
? e.candidate.address
59+
: e.candidate.candidate.match(ip);
60+
IP.innerHTML = '🟢 Your Public IP Address is ' + address;
61+
Stun.innerHTML = '🟢 The STUN server is reachable!';
62+
}
63+
64+
// Detect and indicate if a TURN (relay) candidate is found
65+
if (e.candidate.type == 'relay' || e.candidate.candidate.includes('relay')) {
66+
Turn.innerHTML = '🟢 The TURN server is reachable!';
67+
}
68+
};
69+
70+
// Close the connection when ICE gathering is complete.
71+
pc.onicegatheringstatechange = () => {
72+
if (pc.iceGatheringState === 'complete') {
73+
pc.close();
74+
}
75+
};
76+
77+
// Handle any ICE candidate errors.
78+
pc.onicecandidateerror = (e) => {
79+
console.error(e);
80+
};
81+
82+
// Create a dummy data channel (required to initiate ICE gathering)
83+
pc.createDataChannel('test');
84+
85+
// Start ICE gathering by creating and setting a local offer
86+
pc.createOffer().then(offer => pc.setLocalDescription(offer));
87+
}
88+
89+
// Fetches a temporary token (JWT) from the server.
90+
async function _getToken() {
91+
try {
92+
// Request the credentials from the server API
93+
const response = await fetch('/api/credentials', { method: "GET" });
94+
95+
// Check if the response is successful (HTTP 2xx)
96+
if (!response.ok) {
97+
throw new Error(`Failed to fetch token: ${response.status} ${response.statusText}`);
98+
}
99+
100+
// Parse the response as JSON
101+
const data = await response.json();
102+
103+
// Extract the token from the response
104+
const token = data?.token;
105+
if (!token) {
106+
throw new Error('No token found in server response.');
107+
}
108+
109+
// Return token
110+
return token;
111+
}
112+
catch (err) {
113+
// Log and rethrow for higher-level error handling
114+
console.error('Error fetching token:', err);
115+
}
116+
}
117+
118+
// Decodes the payload of a JWT
119+
async function _decodeJwt(token) {
120+
const payload = token.split(".")[1];
121+
const json = atob(payload);
122+
return JSON.parse(json);
123+
}
124+
</script>
125+
</body>
126+
</html>

0 commit comments

Comments
 (0)