1+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
2+ // UI Elemente
3+ const form = document . getElementById ( 'httpForm' ) ;
4+ const headerContainer = document . getElementById ( 'headerContainer' ) ;
5+ const addHeaderBtn = document . getElementById ( 'addHeaderBtn' ) ;
6+ const submitBtn = document . getElementById ( 'submitBtn' ) ;
7+ const resultArea = document . getElementById ( 'resultArea' ) ;
8+ const responseOutput = document . getElementById ( 'responseOutput' ) ;
9+ const errorBox = document . getElementById ( 'errorBox' ) ;
10+ const statusBadge = document . getElementById ( 'statusBadge' ) ;
11+ const responseTimeText = document . getElementById ( 'responseTime' ) ;
12+ const stacktraceArea = document . getElementById ( 'stacktraceArea' ) ;
13+
14+ // Historie Elemente
15+ const historyList = document . getElementById ( 'historyList' ) ;
16+ const emptyHistoryMsg = document . getElementById ( 'emptyHistoryMsg' ) ;
17+ const clearHistoryBtn = document . getElementById ( 'clearHistoryBtn' ) ;
18+ const exportHistoryBtn = document . getElementById ( 'exportHistoryBtn' ) ;
19+
20+ let requestHistory = [ ] ;
21+
22+ // --- INITIALISIERUNG ---
23+ addHeaderRow ( 'Content-Type' , 'application/json' ) ;
24+
25+ // --- EVENT LISTENER ---
26+ addHeaderBtn . addEventListener ( 'click' , ( ) => addHeaderRow ( ) ) ;
27+
28+ document . getElementById ( 'toggleStackBtn' ) . addEventListener ( 'click' , ( ) => {
29+ stacktraceArea . style . display = stacktraceArea . style . display === 'none' ? 'block' : 'none' ;
30+ } ) ;
31+
32+ clearHistoryBtn . addEventListener ( 'click' , ( ) => {
33+ requestHistory = [ ] ;
34+ renderHistory ( ) ;
35+ emptyHistoryMsg . style . display = 'block' ;
36+ } ) ;
37+
38+ exportHistoryBtn . addEventListener ( 'click' , exportHistoryToJson ) ;
39+
40+ form . addEventListener ( 'submit' , async ( e ) => {
41+ e . preventDefault ( ) ;
42+ await performRequest ( ) ;
43+ } ) ;
44+
45+ // --- CORE FUNKTIONEN ---
46+
47+ async function performRequest ( ) {
48+ const startTime = Date . now ( ) ;
49+ const payload = {
50+ url : document . getElementById ( 'url' ) . value ,
51+ method : document . getElementById ( 'method' ) . value ,
52+ body : document . getElementById ( 'body' ) . value ,
53+ copyHeaders : document . getElementById ( 'copyHeaders' ) . checked ,
54+ customHeaders : collectHeaders ( )
55+ } ;
56+
57+ prepareUIForRequest ( ) ;
58+
59+ try {
60+ const response = await fetch ( '/client' , {
61+ method : 'POST' ,
62+ headers : { 'Content-Type' : 'application/json' } ,
63+ body : JSON . stringify ( payload )
64+ } ) ;
65+
66+ const duration = Date . now ( ) - startTime ;
67+ const data = await response . text ( ) ;
68+
69+ updateResponseMetadata ( response . status , duration ) ;
70+ addToHistory ( payload , response . status , duration , data ) ;
71+
72+ if ( response . status === 502 && data . includes ( "---STACKTRACE---" ) ) {
73+ handleDetailedError ( data ) ;
74+ } else {
75+ handleSuccess ( data ) ;
76+ }
77+ } catch ( err ) {
78+ const errorMsg = "Netzwerkfehler zum Proxy: " + err . message ;
79+ handleNetworkError ( errorMsg ) ;
80+ addToHistory ( payload , 0 , 0 , errorMsg ) ;
81+ } finally {
82+ submitBtn . disabled = false ;
83+ }
84+ }
85+
86+ // --- HILFSFUNKTIONEN ---
87+
88+ function addHeaderRow ( key = '' , value = '' ) {
89+ const div = document . createElement ( 'div' ) ;
90+ div . className = 'row g-2 mb-2 header-row' ;
91+ div . innerHTML = `
92+ <div class="col-5"><input type="text" class="form-control form-control-sm h-key" placeholder="Key" value="${ key } "></div>
93+ <div class="col-6"><input type="text" class="form-control form-control-sm h-val" placeholder="Value" value="${ value } "></div>
94+ <div class="col-1 text-end"><button type="button" class="btn btn-sm btn-outline-danger remove-header">✕</button></div>
95+ ` ;
96+ div . querySelector ( '.remove-header' ) . onclick = ( ) => div . remove ( ) ;
97+ headerContainer . appendChild ( div ) ;
98+ }
99+
100+ function collectHeaders ( ) {
101+ const headers = { } ;
102+ document . querySelectorAll ( '.header-row' ) . forEach ( row => {
103+ const k = row . querySelector ( '.h-key' ) . value . trim ( ) ;
104+ const v = row . querySelector ( '.h-val' ) . value . trim ( ) ;
105+ if ( k ) headers [ k ] = v ;
106+ } ) ;
107+ return headers ;
108+ }
109+
110+ function prepareUIForRequest ( ) {
111+ submitBtn . disabled = true ;
112+ resultArea . style . display = 'block' ;
113+ errorBox . style . display = 'none' ;
114+ responseOutput . style . display = 'block' ;
115+ responseOutput . innerText = "Sende Request an Cluster..." ;
116+ stacktraceArea . style . display = 'none' ;
117+ }
118+
119+ function updateResponseMetadata ( status , duration ) {
120+ statusBadge . innerText = `HTTP ${ status } ` ;
121+ statusBadge . className = `badge p-2 ${ status >= 200 && status < 300 ? 'bg-success' : 'bg-danger' } ` ;
122+ responseTimeText . innerText = `Dauer: ${ duration } ms` ;
123+ }
124+
125+ function handleDetailedError ( data ) {
126+ const [ info , stack ] = data . split ( "---STACKTRACE---" ) ;
127+ const lines = info . split ( "\n" ) ;
128+ errorBox . style . display = 'block' ;
129+ responseOutput . style . display = 'none' ;
130+ document . getElementById ( 'errorSummary' ) . innerText = lines [ 0 ] ;
131+ document . getElementById ( 'errorDetail' ) . innerText = lines [ 1 ] || "" ;
132+ stacktraceArea . innerText = stack . trim ( ) ;
133+ }
134+
135+ function handleSuccess ( data ) {
136+ errorBox . style . display = 'none' ;
137+ responseOutput . style . display = 'block' ;
138+ try {
139+ const json = JSON . parse ( data ) ;
140+ responseOutput . innerText = JSON . stringify ( json , null , 2 ) ;
141+ } catch {
142+ responseOutput . innerText = data || "Empty Response" ;
143+ }
144+ }
145+
146+ function handleNetworkError ( msg ) {
147+ responseOutput . innerText = msg ;
148+ statusBadge . innerText = "Error" ;
149+ statusBadge . className = "badge bg-warning text-dark" ;
150+ }
151+
152+ // --- HISTORIE & EXPORT ---
153+
154+ function addToHistory ( payload , status , duration , responseData ) {
155+ const entry = {
156+ id : Date . now ( ) ,
157+ time : new Date ( ) . toLocaleTimeString ( ) ,
158+ payload, status, duration, responseData
159+ } ;
160+ requestHistory . unshift ( entry ) ;
161+ renderHistory ( ) ;
162+ }
163+
164+ function renderHistory ( ) {
165+ emptyHistoryMsg . style . display = requestHistory . length ? 'none' : 'block' ;
166+ historyList . innerHTML = '' ;
167+ requestHistory . forEach ( entry => {
168+ const isErr = entry . status >= 400 || entry . status === 0 ;
169+ const item = document . createElement ( 'button' ) ;
170+ item . className = `list-group-item list-group-item-action border-start border-4 ${ isErr ? 'border-danger' : 'border-success' } ` ;
171+ item . innerHTML = `
172+ <div class="d-flex justify-content-between"><strong>${ entry . payload . method } </strong><small>${ entry . time } </small></div>
173+ <div class="text-truncate small">${ entry . payload . url } </div>
174+ <div class="mt-1"><span class="badge ${ isErr ? 'bg-danger' : 'bg-success' } ">${ entry . status } </span> <small>${ entry . duration } ms</small></div>
175+ ` ;
176+ item . onclick = ( ) => restoreEntry ( entry ) ;
177+ historyList . appendChild ( item ) ;
178+ } ) ;
179+ }
180+
181+ function restoreEntry ( entry ) {
182+ document . getElementById ( 'url' ) . value = entry . payload . url ;
183+ document . getElementById ( 'method' ) . value = entry . payload . method ;
184+ document . getElementById ( 'body' ) . value = entry . payload . body ;
185+ document . getElementById ( 'copyHeaders' ) . checked = entry . payload . copyHeaders ;
186+
187+ headerContainer . innerHTML = '' ;
188+ Object . entries ( entry . payload . customHeaders ) . forEach ( ( [ k , v ] ) => addHeaderRow ( k , v ) ) ;
189+
190+ resultArea . style . display = 'block' ;
191+ updateResponseMetadata ( entry . status , entry . duration ) ;
192+ if ( entry . status === 502 && entry . responseData . includes ( "---STACKTRACE---" ) ) {
193+ handleDetailedError ( entry . responseData ) ;
194+ } else {
195+ handleSuccess ( entry . responseData ) ;
196+ }
197+ }
198+
199+ function exportHistoryToJson ( ) {
200+ if ( ! requestHistory . length ) return alert ( "Historie ist leer!" ) ;
201+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent ( JSON . stringify ( requestHistory , null , 2 ) ) ;
202+ const downloadAnchor = document . createElement ( 'a' ) ;
203+ downloadAnchor . setAttribute ( "href" , dataStr ) ;
204+ downloadAnchor . setAttribute ( "download" , `k8s_http_history_${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } .json` ) ;
205+ document . body . appendChild ( downloadAnchor ) ;
206+ downloadAnchor . click ( ) ;
207+ downloadAnchor . remove ( ) ;
208+ }
209+ } ) ;
0 commit comments