mirror of
https://github.com/MercuryWorkshop/adrift.git
synced 2025-05-13 06:10:01 -04:00
use tracker-list on client and improve interstitials
This commit is contained in:
parent
494ef6a4a9
commit
9ec17943c2
9 changed files with 307 additions and 260 deletions
|
@ -14,19 +14,19 @@
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
SegmentedButtonContainer,
|
CircularProgressIndeterminate,
|
||||||
SegmentedButtonItem,
|
|
||||||
StyleFromScheme,
|
StyleFromScheme,
|
||||||
TextField,
|
TextField,
|
||||||
} from "m3-svelte";
|
} from "m3-svelte";
|
||||||
// note: even though we import firebase, due to the tree shaking, it will only run if we use "auth" so if ADRIFT_DEV is set it won't import
|
|
||||||
// import { auth } from "firebase-config";
|
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
|
||||||
import { signInWithEmailAndPassword } from "firebase/auth";
|
|
||||||
import { getDatabase, onValue, ref, set } from "firebase/database";
|
import { getDatabase, onValue, ref, set } from "firebase/database";
|
||||||
import type { Transport } from "protocol";
|
import type { Transport } from "protocol";
|
||||||
|
|
||||||
import { Win, openWindow } from "../../corium";
|
import Proxy from "./Proxy.svelte";
|
||||||
|
import { initializeApp } from "firebase/app";
|
||||||
|
|
||||||
|
import TrackerList from "tracker-list";
|
||||||
let transport: Transport;
|
let transport: Transport;
|
||||||
|
|
||||||
let rtctransport: RTCTransport | undefined;
|
let rtctransport: RTCTransport | undefined;
|
||||||
|
@ -34,26 +34,7 @@
|
||||||
let email = "test@test.com";
|
let email = "test@test.com";
|
||||||
let password = "123456";
|
let password = "123456";
|
||||||
|
|
||||||
let ready = false;
|
let connectionState = "";
|
||||||
|
|
||||||
let selectedProxy = "ultraviolet";
|
|
||||||
|
|
||||||
let url: string = "http://google.com";
|
|
||||||
let proxyIframe: HTMLIFrameElement;
|
|
||||||
|
|
||||||
let rtcState = "";
|
|
||||||
|
|
||||||
if (import.meta.env.VITE_ADRIFT_DEV) {
|
|
||||||
console.log(
|
|
||||||
"%cADRIFT RUNNING IN DEVELOPMENT MODE",
|
|
||||||
"background: blue; color: white; font-size: x-large"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
"%cADRIFT RUNNING IN PRODUCTION MODE",
|
|
||||||
"background: blue; color: white; font-size: x-large"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!import.meta.env.VITE_ADRIFT_SINGLEFILE) {
|
if (!import.meta.env.VITE_ADRIFT_SINGLEFILE) {
|
||||||
console.log("registering bare-client-custom");
|
console.log("registering bare-client-custom");
|
||||||
|
@ -67,7 +48,7 @@
|
||||||
let bare = new AdriftBareClient(connection);
|
let bare = new AdriftBareClient(connection);
|
||||||
console.log(setBareClientImplementation);
|
console.log(setBareClientImplementation);
|
||||||
setBareClientImplementation(bare);
|
setBareClientImplementation(bare);
|
||||||
ready = true;
|
state = ReadyState.Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTransportClose() {
|
function onTransportClose() {
|
||||||
|
@ -79,31 +60,41 @@
|
||||||
onTransportOpen,
|
onTransportOpen,
|
||||||
onTransportClose,
|
onTransportClose,
|
||||||
() => {
|
() => {
|
||||||
rtcState = `Connection ${transport.peer.connectionState}`;
|
connectionState = `Connection ${transport.peer.connectionState}`;
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
rtcState = `Signaling ${transport.peer.connectionState}`;
|
connectionState = `Signaling ${transport.peer.connectionState}`;
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
rtcState = `Gathering ${transport.peer.connectionState}`;
|
connectionState = `Gathering ${transport.peer.connectionState}`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return transport;
|
return transport;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function connectFirebase() {
|
async function initFirebase() {
|
||||||
|
let tracker = TrackerList["us-central-1"];
|
||||||
|
initializeApp(tracker.firebase);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectAccount() {
|
||||||
|
await initFirebase();
|
||||||
rtctransport = transport = createRTCTransport();
|
rtctransport = transport = createRTCTransport();
|
||||||
|
|
||||||
|
let auth = getAuth();
|
||||||
let creds = await signInWithEmailAndPassword(auth, email, password);
|
let creds = await signInWithEmailAndPassword(auth, email, password);
|
||||||
|
state = ReadyState.Connecting;
|
||||||
|
|
||||||
const db = getDatabase();
|
const db = getDatabase();
|
||||||
let peer = ref(db, `/peers/${creds.user.uid}`);
|
let peer = ref(db, `/peers/${creds.user.uid}`);
|
||||||
|
|
||||||
let offer = await rtctransport.createOffer();
|
let offer = await rtctransport.createOffer();
|
||||||
|
|
||||||
|
connectionState = "Finding your node...";
|
||||||
|
|
||||||
set(peer, JSON.stringify(offer));
|
set(peer, JSON.stringify(offer));
|
||||||
|
|
||||||
onValue(peer, (snapshot) => {
|
onValue(peer, async (snapshot) => {
|
||||||
const str = snapshot.val();
|
const str = snapshot.val();
|
||||||
if (str) {
|
if (str) {
|
||||||
console.log(str);
|
console.log(str);
|
||||||
|
@ -112,6 +103,10 @@
|
||||||
if (data && data.answer && data.candidates) {
|
if (data && data.answer && data.candidates) {
|
||||||
set(peer, null);
|
set(peer, null);
|
||||||
const { answer, candidates } = data;
|
const { answer, candidates } = data;
|
||||||
|
connectionState = "Linking to node...";
|
||||||
|
await new Promise((r) => {
|
||||||
|
setTimeout(r, 500);
|
||||||
|
});
|
||||||
rtctransport?.answer(answer, candidates);
|
rtctransport?.answer(answer, candidates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,10 +114,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function connectSwarm() {
|
async function connectSwarm() {
|
||||||
|
await initFirebase();
|
||||||
|
|
||||||
|
state = ReadyState.Connecting;
|
||||||
|
|
||||||
rtctransport = transport = createRTCTransport();
|
rtctransport = transport = createRTCTransport();
|
||||||
|
|
||||||
let offer = await rtctransport.createOffer();
|
let offer = await rtctransport.createOffer();
|
||||||
|
connectionState = "Routing you to an available node...";
|
||||||
|
|
||||||
let answer = await SignalFirebase.signalSwarm(JSON.stringify(offer));
|
let answer = await SignalFirebase.signalSwarm(JSON.stringify(offer));
|
||||||
|
connectionState = "Linking to node...";
|
||||||
|
await new Promise((r) => {
|
||||||
|
setTimeout(r, 500);
|
||||||
|
});
|
||||||
|
|
||||||
rtctransport.answer(answer.answer, answer.candidates);
|
rtctransport.answer(answer.answer, answer.candidates);
|
||||||
}
|
}
|
||||||
|
@ -151,36 +156,6 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function visitURL(url: string) {
|
|
||||||
if (!import.meta.env.VITE_ADRIFT_SINGLEFILE) {
|
|
||||||
let path =
|
|
||||||
selectedProxy == "dynamic"
|
|
||||||
? `/service/route?url=${url}`
|
|
||||||
: `${__uv$config.prefix}${__uv$config.encodeUrl(url)}`;
|
|
||||||
|
|
||||||
proxyIframe.src = path;
|
|
||||||
} else {
|
|
||||||
let bare = new BareClient();
|
|
||||||
openWindow(
|
|
||||||
new Request(url),
|
|
||||||
"_self",
|
|
||||||
proxyIframe.contentWindow! as unknown as Win,
|
|
||||||
bare as any,
|
|
||||||
"replace"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function frameLoad() {
|
|
||||||
if (!import.meta.env.VITE_ADRIFT_SINGLEFILE) {
|
|
||||||
const location = proxyIframe.contentDocument?.location.href;
|
|
||||||
if (location && location != "about:blank") {
|
|
||||||
url = __uv$config.decodeUrl(
|
|
||||||
proxyIframe.contentDocument?.location.href.replace(/.*\//g, "")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(window as any).bare = new BareClient();
|
(window as any).bare = new BareClient();
|
||||||
(window as any).myWsTest = () => {
|
(window as any).myWsTest = () => {
|
||||||
// const url = "wss://ws.postman-echo.com/raw";
|
// const url = "wss://ws.postman-echo.com/raw";
|
||||||
|
@ -194,42 +169,22 @@
|
||||||
ws.addEventListener("close", (e) => console.log("close listener", e));
|
ws.addEventListener("close", (e) => console.log("close listener", e));
|
||||||
ws.onmessage = (e) => console.log("message", e);
|
ws.onmessage = (e) => console.log("message", e);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ReadyState {
|
||||||
|
Idle,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
}
|
||||||
|
let state = ReadyState.Idle;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if ready}
|
{#if state == ReadyState.Connected}
|
||||||
<div class="container h-full w-full">
|
<Proxy />
|
||||||
<div class="flex">
|
{:else if state == ReadyState.Connecting}
|
||||||
<div class="container">
|
<CircularProgressIndeterminate />
|
||||||
<input bind:value={url} type="text" />
|
<h2>
|
||||||
<button on:click={() => visitURL(url)}>Go!</button>
|
{connectionState}
|
||||||
</div>
|
</h2>
|
||||||
{#if !import.meta.env.VITE_ADRIFT_SINGLEFILE}
|
|
||||||
<div>
|
|
||||||
<SegmentedButtonContainer>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="selectedProxy"
|
|
||||||
bind:group={selectedProxy}
|
|
||||||
value="ultraviolet"
|
|
||||||
id="ultraviolet"
|
|
||||||
/>
|
|
||||||
<SegmentedButtonItem input="ultraviolet"
|
|
||||||
>Ultraviolet</SegmentedButtonItem
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="selectedProxy"
|
|
||||||
bind:group={selectedProxy}
|
|
||||||
value="dynamic"
|
|
||||||
id="dynamic"
|
|
||||||
/>
|
|
||||||
<SegmentedButtonItem input="dynamic">Dynamic</SegmentedButtonItem>
|
|
||||||
</SegmentedButtonContainer>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<iframe class="h-full w-full" bind:this={proxyIframe} on:load={frameLoad} />
|
|
||||||
</div>
|
|
||||||
{:else if !import.meta.env.VITE_ADRIFT_DEV}
|
{:else if !import.meta.env.VITE_ADRIFT_DEV}
|
||||||
<div id="loginpage">
|
<div id="loginpage">
|
||||||
<div class="bigcard">
|
<div class="bigcard">
|
||||||
|
@ -247,15 +202,15 @@
|
||||||
extraOptions={{ type: "password" }}
|
extraOptions={{ type: "password" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button type="outlined" on:click={connectFirebase}
|
<Button type="outlined" on:click={connectAccount}
|
||||||
>Connect with firebase</Button
|
>Connect with firebase</Button
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<Button type="filled" on:click={connectSwarm}
|
||||||
|
>Connect with the swarm (firebase, webrtc, insecure)
|
||||||
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>
|
|
||||||
{rtcState}
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex items-center justify-center h-full">
|
<div class="flex items-center justify-center h-full">
|
||||||
|
@ -269,15 +224,7 @@
|
||||||
<Button type="filled" on:click={connectDevWS}
|
<Button type="filled" on:click={connectDevWS}
|
||||||
>Connect with localhost websocket transport</Button
|
>Connect with localhost websocket transport</Button
|
||||||
>
|
>
|
||||||
|
|
||||||
<Button type="filled" on:click={connectSwarm}
|
|
||||||
>Connect with the swarm (webrtc, insecure)
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>
|
|
||||||
{rtcState}
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
76
frontend/src/Proxy.svelte
Normal file
76
frontend/src/Proxy.svelte
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { BareClient } from "bare-client-custom";
|
||||||
|
import { SegmentedButtonContainer, SegmentedButtonItem } from "m3-svelte";
|
||||||
|
import { Win, openWindow } from "../../corium";
|
||||||
|
|
||||||
|
let selectedProxy = "ultraviolet";
|
||||||
|
|
||||||
|
let url: string = "http://google.com";
|
||||||
|
|
||||||
|
let proxyIframe: HTMLIFrameElement;
|
||||||
|
|
||||||
|
function frameLoad() {
|
||||||
|
if (!import.meta.env.VITE_ADRIFT_SINGLEFILE) {
|
||||||
|
const location = proxyIframe.contentDocument?.location.href;
|
||||||
|
if (location && location != "about:blank") {
|
||||||
|
url = __uv$config.decodeUrl(
|
||||||
|
proxyIframe.contentDocument?.location.href.replace(/.*\//g, "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function visitURL(url: string) {
|
||||||
|
if (!import.meta.env.VITE_ADRIFT_SINGLEFILE) {
|
||||||
|
let path =
|
||||||
|
selectedProxy == "dynamic"
|
||||||
|
? `/service/route?url=${url}`
|
||||||
|
: `${__uv$config.prefix}${__uv$config.encodeUrl(url)}`;
|
||||||
|
|
||||||
|
proxyIframe.src = path;
|
||||||
|
} else {
|
||||||
|
let bare = new BareClient();
|
||||||
|
openWindow(
|
||||||
|
new Request(url),
|
||||||
|
"_self",
|
||||||
|
proxyIframe.contentWindow! as unknown as Win,
|
||||||
|
bare as any,
|
||||||
|
"replace"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container h-full w-full">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="container">
|
||||||
|
<input bind:value={url} type="text" />
|
||||||
|
<button on:click={() => visitURL(url)}>Go!</button>
|
||||||
|
</div>
|
||||||
|
{#if !import.meta.env.VITE_ADRIFT_SINGLEFILE}
|
||||||
|
<div>
|
||||||
|
<SegmentedButtonContainer>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="selectedProxy"
|
||||||
|
bind:group={selectedProxy}
|
||||||
|
value="ultraviolet"
|
||||||
|
id="ultraviolet"
|
||||||
|
/>
|
||||||
|
<SegmentedButtonItem input="ultraviolet"
|
||||||
|
>Ultraviolet</SegmentedButtonItem
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="selectedProxy"
|
||||||
|
bind:group={selectedProxy}
|
||||||
|
value="dynamic"
|
||||||
|
id="dynamic"
|
||||||
|
/>
|
||||||
|
<SegmentedButtonItem input="dynamic">Dynamic</SegmentedButtonItem>
|
||||||
|
</SegmentedButtonContainer>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<iframe class="h-full w-full" bind:this={proxyIframe} on:load={frameLoad} />
|
||||||
|
</div>
|
|
@ -1,5 +1,16 @@
|
||||||
import App from "./App.svelte";
|
import App from "./App.svelte";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
if (import.meta.env.VITE_ADRIFT_DEV) {
|
||||||
|
console.log(
|
||||||
|
"%cADRIFT RUNNING IN DEVELOPMENT MODE",
|
||||||
|
"background: blue; color: white; font-size: x-large"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"%cADRIFT RUNNING IN PRODUCTION MODE",
|
||||||
|
"background: blue; color: white; font-size: x-large"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
target: document.getElementById("app")!,
|
target: document.getElementById("app")!,
|
||||||
|
|
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
|
@ -286,12 +286,6 @@ importers:
|
||||||
specifier: ^5.0.1
|
specifier: ^5.0.1
|
||||||
version: 5.0.1(webpack@5.75.0)
|
version: 5.0.1(webpack@5.75.0)
|
||||||
|
|
||||||
firebase-config:
|
|
||||||
dependencies:
|
|
||||||
firebase:
|
|
||||||
specifier: ^10.1.0
|
|
||||||
version: 10.1.0(react-native@0.72.3)
|
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/browser':
|
'@rollup/browser':
|
||||||
|
@ -321,9 +315,6 @@ importers:
|
||||||
firebase:
|
firebase:
|
||||||
specifier: ^10.1.0
|
specifier: ^10.1.0
|
||||||
version: 10.1.0(react-native@0.72.3)
|
version: 10.1.0(react-native@0.72.3)
|
||||||
firebase-config:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../firebase-config
|
|
||||||
m3-svelte:
|
m3-svelte:
|
||||||
specifier: ^2.0.3
|
specifier: ^2.0.3
|
||||||
version: 2.0.3
|
version: 2.0.3
|
||||||
|
@ -345,6 +336,9 @@ importers:
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
version: 3.3.3
|
version: 3.3.3
|
||||||
|
tracker-list:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../tracker-list
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.1.6
|
specifier: ^5.1.6
|
||||||
version: 5.1.6
|
version: 5.1.6
|
||||||
|
@ -377,9 +371,6 @@ importers:
|
||||||
firebase:
|
firebase:
|
||||||
specifier: ^10.1.0
|
specifier: ^10.1.0
|
||||||
version: 10.1.0(react-native@0.72.3)
|
version: 10.1.0(react-native@0.72.3)
|
||||||
firebase-config:
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../firebase-config
|
|
||||||
ipaddr.js:
|
ipaddr.js:
|
||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
|
@ -389,6 +380,9 @@ importers:
|
||||||
protocol:
|
protocol:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../protocol
|
version: link:../protocol
|
||||||
|
tracker-list:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../tracker-list
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.1
|
specifier: ^10.9.1
|
||||||
version: 10.9.1(@types/node@20.4.10)(typescript@5.1.6)
|
version: 10.9.1(@types/node@20.4.10)(typescript@5.1.6)
|
||||||
|
@ -435,9 +429,9 @@ importers:
|
||||||
firebase-admin:
|
firebase-admin:
|
||||||
specifier: ^11.10.1
|
specifier: ^11.10.1
|
||||||
version: 11.10.1
|
version: 11.10.1
|
||||||
firebase-config:
|
tracker-list:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../firebase-config
|
version: link:../tracker-list
|
||||||
ts-node:
|
ts-node:
|
||||||
specifier: ^10.9.1
|
specifier: ^10.9.1
|
||||||
version: 10.9.1(@types/node@20.4.10)(typescript@5.1.6)
|
version: 10.9.1(@types/node@20.4.10)(typescript@5.1.6)
|
||||||
|
@ -464,6 +458,8 @@ importers:
|
||||||
specifier: ^3.0.1
|
specifier: ^3.0.1
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
|
|
||||||
|
tracker-list: {}
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
/@aashutoshrathi/word-wrap@1.2.6:
|
/@aashutoshrathi/word-wrap@1.2.6:
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
|
||||||
|
|
||||||
|
|
||||||
|
import { getDatabase, onValue, ref, set } from "firebase/database";
|
||||||
|
import { answerRtc } from "./rtc";
|
||||||
|
|
||||||
|
async function connectFirebase() {
|
||||||
|
let creds = await signInWithEmailAndPassword(getAuth(), "test@test.com", "123456");
|
||||||
|
|
||||||
|
const db = getDatabase();
|
||||||
|
let peer = ref(db, `/peers/${creds.user.uid}`);
|
||||||
|
|
||||||
|
set(peer, "");
|
||||||
|
|
||||||
|
onValue(peer, (snapshot) => {
|
||||||
|
const str = snapshot.val();
|
||||||
|
|
||||||
|
if (str) {
|
||||||
|
let data = JSON.parse(str);
|
||||||
|
if (data && data.offer && data.localCandidates) {
|
||||||
|
answerRtc(data, (answer) => {
|
||||||
|
console.log("answering");
|
||||||
|
set(peer, JSON.stringify(answer));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
connectFirebase();
|
|
@ -2,72 +2,13 @@ import dotenv from "dotenv";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import expressWs from "express-ws";
|
import expressWs from "express-ws";
|
||||||
|
|
||||||
import { signInWithEmailAndPassword } from "firebase/auth";
|
import { AdriftServer, connectTracker } from "./server";
|
||||||
import wrtc from "wrtc";
|
|
||||||
|
|
||||||
|
|
||||||
import { getDatabase, onValue, ref, set } from "firebase/database";
|
|
||||||
import { AdriftServer } from "./server";
|
|
||||||
|
|
||||||
import WebSocket from "isomorphic-ws";
|
import WebSocket from "isomorphic-ws";
|
||||||
const configuration = {
|
import { answerRtc, bufferToArrayBuffer, connect } from "./rtc";
|
||||||
iceServers: [
|
|
||||||
{
|
|
||||||
urls: "stun:stun.l.google.com:19302",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
async function connect(
|
|
||||||
offer: RTCSessionDescriptionInit,
|
|
||||||
candidates: RTCIceCandidateInit[],
|
|
||||||
onAnswer: (answer: Record<string, any>) => void
|
|
||||||
): Promise<RTCDataChannel> {
|
|
||||||
const localCandidates: any[] = [];
|
|
||||||
let dataChannel;
|
|
||||||
const peer: RTCPeerConnection = new wrtc.RTCPeerConnection(configuration);
|
|
||||||
let promise = new Promise((resolve) => {
|
|
||||||
peer.ondatachannel = (event) => {
|
|
||||||
dataChannel = event.channel;
|
|
||||||
resolve(dataChannel);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
peer.onconnectionstatechange = () => {
|
|
||||||
console.log("Connection state:", peer.connectionState);
|
|
||||||
};
|
|
||||||
peer.onsignalingstatechange = () => {
|
|
||||||
console.log("Signaling state:", peer.signalingState);
|
|
||||||
};
|
|
||||||
peer.oniceconnectionstatechange = () => {
|
|
||||||
console.log("ICE connection state:", peer.iceConnectionState);
|
|
||||||
};
|
|
||||||
peer.onicegatheringstatechange = () => {
|
|
||||||
console.log("ICE gathering state:", peer.iceGatheringState);
|
|
||||||
};
|
|
||||||
peer.onicecandidate = (event: any) => {
|
|
||||||
console.log("onicecandidate");
|
|
||||||
if (event.candidate) {
|
|
||||||
localCandidates.push(event.candidate);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let payload = {
|
|
||||||
answer: peer.localDescription,
|
|
||||||
candidates: localCandidates,
|
|
||||||
};
|
|
||||||
onAnswer(payload);
|
|
||||||
};
|
|
||||||
await peer.setRemoteDescription(offer);
|
|
||||||
let answer = await peer.createAnswer();
|
|
||||||
await peer.setLocalDescription(answer);
|
|
||||||
for (let candidate of candidates) {
|
|
||||||
if (!candidate.candidate) continue;
|
|
||||||
console.log({ candidate });
|
|
||||||
await peer.addIceCandidate(candidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
return promise as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = express() as unknown as expressWs.Application;
|
const app = express() as unknown as expressWs.Application;
|
||||||
expressWs(app);
|
expressWs(app);
|
||||||
|
@ -82,48 +23,8 @@ app.use((_req, res, next) => {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
function bufferToArrayBuffer(buf: Buffer): ArrayBuffer {
|
|
||||||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function answerRtc(data: any, onrespond: (answer: any) => void) {
|
|
||||||
if (data && data.offer && data.localCandidates) {
|
|
||||||
const { offer, localCandidates } = data;
|
|
||||||
let didAnswer = false;
|
|
||||||
|
|
||||||
let dataChannel = await connect(offer, localCandidates, (answer) => {
|
|
||||||
if (!didAnswer) {
|
|
||||||
didAnswer = true;
|
|
||||||
onrespond(answer);
|
|
||||||
// res.json(answer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dataChannel.binaryType = "arraybuffer";
|
|
||||||
|
|
||||||
let server: AdriftServer;
|
|
||||||
|
|
||||||
dataChannel.onopen = () => {
|
|
||||||
console.log("opened");
|
|
||||||
server = new AdriftServer((msg) => dataChannel.send(msg));
|
|
||||||
};
|
|
||||||
dataChannel.onclose = () => {
|
|
||||||
console.log("closed");
|
|
||||||
server.onClose();
|
|
||||||
};
|
|
||||||
dataChannel.onmessage = (event) => {
|
|
||||||
console.log("messaged");
|
|
||||||
if (event.data instanceof ArrayBuffer) {
|
|
||||||
server.onMsg(event.data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.data instanceof Buffer) {
|
|
||||||
server.onMsg(bufferToArrayBuffer(event.data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error("Unexpected datachannel message type");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.post("/connect", (req, res) => {
|
app.post("/connect", (req, res) => {
|
||||||
const data = req.body;
|
const data = req.body;
|
||||||
|
@ -149,42 +50,12 @@ app.ws("/dev-ws", (ws, _req) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function connectFirebase() {
|
try {
|
||||||
let creds = await signInWithEmailAndPassword(auth, "test@test.com", "123456");
|
|
||||||
|
|
||||||
const db = getDatabase();
|
|
||||||
let peer = ref(db, `/peers/${creds.user.uid}`);
|
|
||||||
|
|
||||||
set(peer, "");
|
|
||||||
|
|
||||||
onValue(peer, (snapshot) => {
|
|
||||||
const str = snapshot.val();
|
|
||||||
|
|
||||||
if (str) {
|
|
||||||
let data = JSON.parse(str);
|
|
||||||
if (data && data.offer && data.localCandidates) {
|
|
||||||
answerRtc(data, (answer) => {
|
|
||||||
console.log("answering");
|
|
||||||
set(peer, JSON.stringify(answer));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
connectFirebase();
|
|
||||||
|
|
||||||
let tracker = new WebSocket("ws://localhost:17776/join");
|
let tracker = new WebSocket("ws://localhost:17776/join");
|
||||||
tracker.on("message", (str: string) => {
|
connectTracker(tracker);
|
||||||
if (!str) return;
|
} catch (_) {
|
||||||
let data = JSON.parse(str);
|
|
||||||
if (!(data && data.offer && data.localCandidates)) return;
|
|
||||||
console.log("got offer");
|
|
||||||
|
|
||||||
answerRtc(data, (answer) => {
|
}
|
||||||
console.log("have an answer");
|
|
||||||
tracker.send(JSON.stringify(answer));
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000, () => console.log("listening"));
|
app.listen(3000, () => console.log("listening"));
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
const configuration = {
|
||||||
|
iceServers: [
|
||||||
|
{
|
||||||
|
urls: "stun:stun.l.google.com:19302",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
import wrtc from "wrtc";
|
||||||
|
import { AdriftServer } from "./server";
|
||||||
|
|
||||||
|
export async function connect(
|
||||||
|
offer: RTCSessionDescriptionInit,
|
||||||
|
candidates: RTCIceCandidateInit[],
|
||||||
|
onAnswer: (answer: Record<string, any>) => void
|
||||||
|
): Promise<RTCDataChannel> {
|
||||||
|
const localCandidates: any[] = [];
|
||||||
|
let dataChannel;
|
||||||
|
const peer: RTCPeerConnection = new wrtc.RTCPeerConnection(configuration);
|
||||||
|
let promise = new Promise((resolve) => {
|
||||||
|
peer.ondatachannel = (event) => {
|
||||||
|
dataChannel = event.channel;
|
||||||
|
resolve(dataChannel);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
peer.onconnectionstatechange = () => {
|
||||||
|
console.log("Connection state:", peer.connectionState);
|
||||||
|
};
|
||||||
|
peer.onsignalingstatechange = () => {
|
||||||
|
console.log("Signaling state:", peer.signalingState);
|
||||||
|
};
|
||||||
|
peer.oniceconnectionstatechange = () => {
|
||||||
|
console.log("ICE connection state:", peer.iceConnectionState);
|
||||||
|
};
|
||||||
|
peer.onicegatheringstatechange = () => {
|
||||||
|
console.log("ICE gathering state:", peer.iceGatheringState);
|
||||||
|
};
|
||||||
|
peer.onicecandidate = (event: any) => {
|
||||||
|
console.log("onicecandidate");
|
||||||
|
if (event.candidate) {
|
||||||
|
localCandidates.push(event.candidate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let payload = {
|
||||||
|
answer: peer.localDescription,
|
||||||
|
candidates: localCandidates,
|
||||||
|
};
|
||||||
|
onAnswer(payload);
|
||||||
|
};
|
||||||
|
await peer.setRemoteDescription(offer);
|
||||||
|
let answer = await peer.createAnswer();
|
||||||
|
await peer.setLocalDescription(answer);
|
||||||
|
for (let candidate of candidates) {
|
||||||
|
if (!candidate.candidate) continue;
|
||||||
|
console.log({ candidate });
|
||||||
|
await peer.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise as any;
|
||||||
|
}
|
||||||
|
export async function answerRtc(data: any, onrespond: (answer: any) => void) {
|
||||||
|
if (data && data.offer && data.localCandidates) {
|
||||||
|
const { offer, localCandidates } = data;
|
||||||
|
let didAnswer = false;
|
||||||
|
|
||||||
|
let dataChannel = await connect(offer, localCandidates, (answer) => {
|
||||||
|
if (!didAnswer) {
|
||||||
|
didAnswer = true;
|
||||||
|
onrespond(answer);
|
||||||
|
// res.json(answer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dataChannel.binaryType = "arraybuffer";
|
||||||
|
|
||||||
|
let server: AdriftServer;
|
||||||
|
|
||||||
|
dataChannel.onopen = () => {
|
||||||
|
console.log("opened");
|
||||||
|
server = new AdriftServer((msg) => dataChannel.send(msg));
|
||||||
|
};
|
||||||
|
dataChannel.onclose = () => {
|
||||||
|
console.log("closed");
|
||||||
|
server.onClose();
|
||||||
|
};
|
||||||
|
dataChannel.onmessage = (event) => {
|
||||||
|
console.log("messaged");
|
||||||
|
if (event.data instanceof ArrayBuffer) {
|
||||||
|
server.onMsg(event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.data instanceof Buffer) {
|
||||||
|
server.onMsg(bufferToArrayBuffer(event.data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error("Unexpected datachannel message type");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function bufferToArrayBuffer(buf: Buffer): ArrayBuffer {
|
||||||
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
||||||
|
}
|
|
@ -14,6 +14,8 @@ import {
|
||||||
} from "protocol";
|
} from "protocol";
|
||||||
import { Readable, Writable } from "stream";
|
import { Readable, Writable } from "stream";
|
||||||
import { BareError, bareInitialFetch, fetchResponse, options } from "./http";
|
import { BareError, bareInitialFetch, fetchResponse, options } from "./http";
|
||||||
|
import { answerRtc } from "./rtc";
|
||||||
|
|
||||||
|
|
||||||
function bareErrorToResponse(e: BareError): {
|
function bareErrorToResponse(e: BareError): {
|
||||||
payload: HTTPResponsePayload;
|
payload: HTTPResponsePayload;
|
||||||
|
@ -369,3 +371,17 @@ export class AdriftServer {
|
||||||
this.events.emit("close");
|
this.events.emit("close");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function connectTracker(tracker: WebSocket) {
|
||||||
|
tracker.on("message", (str: string) => {
|
||||||
|
if (!str) return;
|
||||||
|
let data = JSON.parse(str);
|
||||||
|
if (!(data && data.offer && data.localCandidates)) return;
|
||||||
|
console.log("got offer");
|
||||||
|
|
||||||
|
answerRtc(data, (answer) => {
|
||||||
|
console.log("have an answer");
|
||||||
|
tracker.send(JSON.stringify(answer));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": false,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue