summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom van der Lee <tom@vanderlee.io>2026-06-30 22:23:27 +0200
committerGravatar Tom van der Lee <tom@vanderlee.io>2026-06-30 22:23:28 +0200
commit95600cf6da1c6fbd5ee9f1a5de57b17503d4fe9c (patch)
treec8c82dc9c59ad04c0b0b66510a6374e207bb85ed
parent5f32db609e618bebd568d48d151a80d865e55859 (diff)
downloadclient-95600cf6da1c6fbd5ee9f1a5de57b17503d4fe9c.tar.gz
client-95600cf6da1c6fbd5ee9f1a5de57b17503d4fe9c.tar.bz2
client-95600cf6da1c6fbd5ee9f1a5de57b17503d4fe9c.zip
Filter based on subdomainsv2.4.1main
-rw-r--r--src/components/App/App.tsx15
-rw-r--r--src/components/RequestList/RequestList.tsx50
-rw-r--r--src/contexts/Connection.tsx2
-rw-r--r--ttun/__main__.py7
-rw-r--r--ttun/client.py8
-rw-r--r--ttun/types.py2
-rw-r--r--uv.lock734
-rw-r--r--yarn.lock840
8 files changed, 887 insertions, 771 deletions
diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx
index 85119e6..2c8025d 100644
--- a/src/components/App/App.tsx
+++ b/src/components/App/App.tsx
@@ -1,5 +1,5 @@
1import * as React from "react"; 1import * as React from "react";
2import { ReactElement, useContext, useEffect } from "react"; 2import { ReactElement, useContext, useEffect, useState } from "react";
3 3
4import styles from "~/components/App/App.module.scss"; 4import styles from "~/components/App/App.module.scss";
5import RequestDetails from "~/components/RequestDetails/RequestDetails"; 5import RequestDetails from "~/components/RequestDetails/RequestDetails";
@@ -28,9 +28,9 @@ type ReadyStateMap = {
28}; 28};
29 29
30const statusIconMap: ReadyStateMap = { 30const statusIconMap: ReadyStateMap = {
31 [ReadyState.CONNECTING]: "🔴", 31 [ReadyState.CONNECTING]: "🟠",
32 [ReadyState.OPEN]: "🟢", 32 [ReadyState.OPEN]: "🟢",
33 [ReadyState.CLOSING]: "🔴", 33 [ReadyState.CLOSING]: "🟠",
34 [ReadyState.CLOSED]: "🔴", 34 [ReadyState.CLOSED]: "🔴",
35}; 35};
36 36
@@ -47,9 +47,9 @@ export default function App() {
47 useContext(ConnectionContext); 47 useContext(ConnectionContext);
48 48
49 useEffect(() => { 49 useEffect(() => {
50 const url = new URL(config?.url ?? "https://loading..."); 50 const url = new URL(config?.urls[0] ?? "https://loading...");
51 document.title = `${statusIconMap[readyState]} ${url.host} | TTUN`; 51 document.title = `${statusIconMap[readyState]} ${url.host} | TTUN`;
52 }, [readyState, config?.url]); 52 }, [readyState, config?.urls]);
53 53
54 const settingsMenu: (SettingsMenu | null)[] = [ 54 const settingsMenu: (SettingsMenu | null)[] = [
55 { 55 {
@@ -80,11 +80,6 @@ export default function App() {
80 </Navbar.Text> 80 </Navbar.Text>
81 </div> 81 </div>
82 <div className="d-flex"> 82 <div className="d-flex">
83 <Navbar.Text>
84 <a href={config.url} target="_blank">
85 {config.url}
86 </a>
87 </Navbar.Text>
88 <Navbar.Toggle aria-controls="settings" /> 83 <Navbar.Toggle aria-controls="settings" />
89 <Navbar.Collapse id="settings" className="ms-2" role="button"> 84 <Navbar.Collapse id="settings" className="ms-2" role="button">
90 <Nav> 85 <Nav>
diff --git a/src/components/RequestList/RequestList.tsx b/src/components/RequestList/RequestList.tsx
index 8217229..1ab53d8 100644
--- a/src/components/RequestList/RequestList.tsx
+++ b/src/components/RequestList/RequestList.tsx
@@ -21,9 +21,14 @@ type EnabledMethods = {
21 [method in Method]: boolean; 21 [method in Method]: boolean;
22}; 22};
23 23
24type EnabledHosts = {
25 [host: string]: boolean;
26};
27
24export default function RequestList() { 28export default function RequestList() {
25 const { darkMode } = useContext(SettingsContext); 29 const { darkMode } = useContext(SettingsContext);
26 const { 30 const {
31 config,
27 calls: requests, 32 calls: requests,
28 selectedCall, 33 selectedCall,
29 setSelectedCall, 34 setSelectedCall,
@@ -33,6 +38,20 @@ export default function RequestList() {
33 const [search, setSearch] = useState(""); 38 const [search, setSearch] = useState("");
34 const [enableRegex, setEnableRegex] = useState(false); 39 const [enableRegex, setEnableRegex] = useState(false);
35 40
41 const [hosts, setHosts] = useState<EnabledHosts>({});
42
43 useEffect(() => {
44 setHosts((oldValues) =>
45 (config?.urls ?? []).reduce((acc, url) => {
46 const { host } = new URL(url);
47 return {
48 ...acc,
49 [host]: oldValues[host] ?? true,
50 };
51 }, {})
52 );
53 }, [config?.urls]);
54
36 const [methods, setMethods] = useState<EnabledMethods>({ 55 const [methods, setMethods] = useState<EnabledMethods>({
37 GET: true, 56 GET: true,
38 HEAD: true, 57 HEAD: true,
@@ -95,6 +114,17 @@ export default function RequestList() {
95 114
96 return requests 115 return requests
97 .reverse() 116 .reverse()
117 .filter((request) => {
118 const [_, hostHeader] = request.request.headers.find(
119 ([key]) => key.toLowerCase() === "host"
120 ) ?? [null, null];
121
122 if (hostHeader === null) {
123 return true;
124 }
125
126 return hosts[hostHeader] ?? false;
127 })
98 .filter( 128 .filter(
99 (request) => 129 (request) =>
100 enabledMethods.length === 0 || 130 enabledMethods.length === 0 ||
@@ -103,7 +133,7 @@ export default function RequestList() {
103 .filter( 133 .filter(
104 (request) => search === "" || searchRegex.test(request.request.path) 134 (request) => search === "" || searchRegex.test(request.request.path)
105 ); 135 );
106 }, [requests, search, enabledMethods, enableRegex]); 136 }, [requests, search, hosts, enabledMethods, enableRegex]);
107 137
108 return ( 138 return (
109 <div className={classNames(styles.listContainer, "d-flex")}> 139 <div className={classNames(styles.listContainer, "d-flex")}>
@@ -205,6 +235,20 @@ export default function RequestList() {
205 /> 235 />
206 </Form.Group> 236 </Form.Group>
207 <Form.Group className="mb-4"> 237 <Form.Group className="mb-4">
238 <Form.Label className="fw-bold">Hosts</Form.Label>
239 {Object.entries(hosts).map(([host, enabled]) => (
240 <Form.Check
241 key={host}
242 type="switch"
243 label={host}
244 checked={enabled}
245 onChange={() =>
246 setHosts((values) => ({ ...values, [host]: !enabled }))
247 }
248 />
249 ))}
250 </Form.Group>
251 <Form.Group className="mb-4">
208 <Form.Label className="fw-bold">Method</Form.Label> 252 <Form.Label className="fw-bold">Method</Form.Label>
209 {Object.entries(methods).map(([method, enabled]) => ( 253 {Object.entries(methods).map(([method, enabled]) => (
210 <Form.Check 254 <Form.Check
@@ -212,7 +256,9 @@ export default function RequestList() {
212 type="switch" 256 type="switch"
213 label={method} 257 label={method}
214 checked={enabled} 258 checked={enabled}
215 onChange={() => setMethods({ ...methods, [method]: !enabled })} 259 onChange={() =>
260 setMethods((values) => ({ ...values, [method]: !enabled }))
261 }
216 /> 262 />
217 ))} 263 ))}
218 </Form.Group> 264 </Form.Group>
diff --git a/src/contexts/Connection.tsx b/src/contexts/Connection.tsx
index 42482e1..37a6a71 100644
--- a/src/contexts/Connection.tsx
+++ b/src/contexts/Connection.tsx
@@ -32,7 +32,7 @@ import {
32import { getHost } from "~/utils"; 32import { getHost } from "~/utils";
33 33
34interface Config { 34interface Config {
35 url: string; 35 urls: string[];
36} 36}
37 37
38interface ConnectionApi { 38interface ConnectionApi {
diff --git a/ttun/__main__.py b/ttun/__main__.py
index 8d77480..4b108f5 100644
--- a/ttun/__main__.py
+++ b/ttun/__main__.py
@@ -87,11 +87,8 @@ def main():
87 def print_info(server: Server): 87 def print_info(server: Server):
88 print("Tunnel created:") 88 print("Tunnel created:")
89 89
90 if "urls" in client.config: 90 for url in client.config["urls"]:
91 for url in client.config["urls"]: 91 print(f"{url} -> {client.proxy_origin}")
92 print(f"{url} -> {client.proxy_origin}")
93 else:
94 print(f'{client.config["url"]} -> {client.proxy_origin}')
95 92
96 print("") 93 print("")
97 print(f"Inspect requests:") 94 print(f"Inspect requests:")
diff --git a/ttun/client.py b/ttun/client.py
index 6aed814..63bf258 100644
--- a/ttun/client.py
+++ b/ttun/client.py
@@ -55,7 +55,7 @@ class Client:
55 self.server = server 55 self.server = server
56 self.subdomains = subdomains 56 self.subdomains = subdomains
57 57
58 self.config: Optional[Config] = None 58 self._config: dict = None
59 self.connection: ClientConnection = None 59 self.connection: ClientConnection = None
60 60
61 self.proxy_origin = f'{"https" if https else "http"}://{to}:{port}' 61 self.proxy_origin = f'{"https" if https else "http"}://{to}:{port}'
@@ -98,10 +98,14 @@ class Client:
98 } 98 }
99 ) 99 )
100 100
101 self.config = await self.receive() 101 self._config = await self.receive()
102