diff --git a/public_html/admin/functions.js b/public_html/admin/functions.js index 7650f5d31ce6382ddfb9581ec680a12c795a0871..b5feeee70541c9c91792d2b95ca4ef8fea5bcf69 100644 --- a/public_html/admin/functions.js +++ b/public_html/admin/functions.js @@ -44,6 +44,12 @@ async function showInOverlay(oLink){ if (response.ok) { show(body); + + // colorize clicked link + var myspan=document.getElementsByClassName("status")[0]; + var myclass=myspan.className.replace("status ",""); + oLink.className=myclass; + oLink.title=myspan.innerText; } else { show("HTTP-Error: " + response.status); } diff --git a/public_html/admin/index.php b/public_html/admin/index.php index 3dfc8c68142921fc7990d9bea72acb19da274b42..185914d25e921b7a67cf44faf8dacd7bad7e3291 100644 --- a/public_html/admin/index.php +++ b/public_html/admin/index.php @@ -14,47 +14,46 @@ * 2022-02-03 v0.1 <axel.hahn@iml.unibe.ch> initial version * 2022-05-31 v0.2 <axel.hahn@iml.unibe.ch> optical changes; use debugredirect=1 if url is a local domain * 2023-08-28 v1.0 <axel.hahn@unibe.ch> Welcome message if there is no config yet + * 2025-01-13 v1.1 <axel.hahn@unibe.ch> test links http and https * ---------------------------------------------------------------------- */ require_once '../classes/redirect.admin.class.php'; -$oR=new redirectadmin(); -$sHtml=''; -$sErrors=''; +$oR = new redirectadmin(); +$sHtml = ''; +$sErrors = ''; -$aIco=[ - 'h1'=>'⏩', +$aIco = [ + 'h2_err' => '⚠️', + 'h2_config' => '🛠️', + 'h2_file' => '📄', - 'h2_head'=>'✔️', - 'h2_err'=>'⚠️', - 'h2_config'=>'🛠️', - 'h2_file'=>'📄', + 'ip_ok' => '🟢', + 'ip_warn' => '🟠', + 'ip_err' => '❗', - 'ip_ok'=>'🟢', - 'ip_warn'=>'🟠', - 'ip_err'=>'❗', + 'type_config' => '🔷', + 'type_alias' => '▪️', - 'type_config'=>'🔷', - 'type_alias'=>'◻️', - - 'url'=>'🌐', - 'welcome'=>'🪄', + 'url' => '🌐', + 'welcome' => '🪄', ]; // ---------------------------------------------------------------------- // FUNCTIONS // ---------------------------------------------------------------------- -function getId($sDomain){ - return 'id_'.md5($sDomain); +function getId($sDomain) +{ + return 'id_' . md5($sDomain); } // ---------------------------------------------------------------------- // MAIN // ---------------------------------------------------------------------- -if (!$oR->isEnabled()){ - $sHtml.='<div class="content"> +if (!$oR->isEnabled()) { + $sHtml .= '<div class="content"> <h3>Nothing to see here.</h3> <div class="error"> The Admin interface is disabled. @@ -66,159 +65,154 @@ if (!$oR->isEnabled()){ // ---------- return content for ajax requests // ---------- TEST URL - $sUrl=(isset($_GET['url']) && $_GET['url']) ? $_GET['url'] : ''; + $sUrl = (isset($_GET['url']) && $_GET['url']) ? $_GET['url'] : ''; - if ($sUrl){ - $sResult=$oR->httpGet($sUrl,1); - echo '<h2>Response of a http HEAD request to '.$aIco['url'].' <a href="'.$sUrl.'">'.$sUrl.'</a></h2>' - . $oR->renderHttpResponseHeader($sResult) - ; + if ($sUrl) { + $aResult = $oR->httpGet($sUrl, 1); + echo '<h2>Response of a http HEAD request to ' . $aIco['url'] . ' <a href="' . $sUrl . '">' . $sUrl . '</a></h2>' + . $oR->renderHttpResponseHeader($aResult) + ; return true; } # ---------- return content for ajax requests - $sCfgfile=(isset($_GET['cfgfile']) && $_GET['cfgfile']) ? $_GET['cfgfile'] : ''; - if($sCfgfile){ - $sFilename=__DIR__.'/../../config/'.$sCfgfile; - echo '<h2>'.$aIco['h2_file'].' File: '.htmlentities($sCfgfile).'</h2>' + $sCfgfile = (isset($_GET['cfgfile']) && $_GET['cfgfile']) ? $_GET['cfgfile'] : ''; + if ($sCfgfile) { + $sFilename = __DIR__ . '/../../config/' . $sCfgfile; + echo '<h2>' . $aIco['h2_file'] . ' File: ' . htmlentities($sCfgfile) . '</h2>' . (file_exists($sFilename) - ? '<pre>'. file_get_contents($sFilename).'</pre>' + ? '<pre>' . file_get_contents($sFilename) . '</pre>' : '<div class="error">ERROR: config file was not found.</div>' ) ; exit(0); } - $sMyIp=gethostbyname($_SERVER['SERVER_NAME']); - if(!$sMyIp){ - $sErrors.='<li>Ip address of current host ['.$_SERVER['SERVER_NAME'].'] was not found.</li>'; + $sMyIp = gethostbyname($_SERVER['SERVER_NAME']); + if (!$sMyIp) { + $sErrors .= '<li>Ip address of current host [' . $_SERVER['SERVER_NAME'] . '] was not found.</li>'; } // ---------- GET CONFIG DATA - $aHosts=$oR->getHosts(); + $aHosts = $oR->getHosts(); // ---------- SHOW ERRORS - if(count($aHosts['_errors'])) { - $sErrors.= '<li>' . implode('</li><li>', $aHosts['_errors']).'</li>'; + if (count($aHosts['_errors'])) { + $sErrors .= '<li>' . implode('</li><li>', $aHosts['_errors']) . '</li>'; } unset($aHosts['_errors']); // ---------- LOOP OVER ALL ENTRIES - $sTable=''; - foreach($aHosts as $sHost => $aCfg){ - $bHttpOnly=isset($aCfg['redirects']['httponly']) && $aCfg['redirects']['httponly']; - - $sUrlpart='://'.$sHost.'/' - .($aCfg['ip']===$sMyIp ? '?debugredirect=1' : '' ) - ; - $sTdFirst='<tr class="cfgtype-'.$aCfg['type'].'">' - .'<td>' - .'<span style="display: none">'.$sHost.'</span>' - .$aIco['type_'.$aCfg['type']] . " $sHost" - .'<span style="float: right;">' - .' <a href="?url=http'.$sUrlpart.'" title="click to test http://'.$sHost.'/" onclick="showInOverlay(this); return false;">http</a> ' - .($bHttpOnly ? '' : '<a href="?url=https'.$sUrlpart.'" title="click to test https://'.$sHost.'/" onclick="showInOverlay(this); return false;">https</a>') - .'</span>' - .'</td>' - .'<td>' - .($aCfg['ip'] - ? ($aCfg['ip']===$sMyIp - ? '<span title="">'.$aIco['ip_ok'].' '.$aCfg['ip']. '</span>' - : '<span title="Warning: this is not the ip address of the current host ('.$sMyIp.')">'.$aIco['ip_warn'].' '.$aCfg['ip']. '</span>' - ) - : '<span class="error">'.$aIco['ip_err'].' ERROR: unknown host</span>') - .'</td>' - .'<td>' - .($aCfg['type']=="config" - ? '<a href="?cfgfile=redirects_'.$sHost.'.json" onclick="showInOverlay(this); return false;" title="show config for host '.$sHost.'">'.$aCfg['type'].'</a> ' - : '<a href="?cfgfile=redirects_'.$aCfg['target'].'.json" onclick="showInOverlay(this); return false;" title="show config for alias '.$sHost.' pointing to host '.$aCfg['target'].'">'.$aCfg['type'].'</a> ' + $sTable = ''; + foreach ($aHosts as $sHost => $aCfg) { + $bHttpOnly = isset($aCfg['redirects']['httponly']) && $aCfg['redirects']['httponly']; + + $sUrlpart = '://' . $sHost . '/' + . ($aCfg['ip'] === $sMyIp ? '?debugredirect=1' : '') + ; + $sTdFirst = '<tr class="cfgtype-' . $aCfg['type'] . '">' + . '<td>' + . '<span style="display: none">' . $sHost . '</span>' + . '<span style="float: right;">' + . ' <a href="?url=http' . $sUrlpart . '" title="click to test http://' . $sHost . '/" onclick="showInOverlay(this); return false;">http</a> ' + . ($bHttpOnly ? '' : '<a href="?url=https' . $sUrlpart . '" title="click to test https://' . $sHost . '/" onclick="showInOverlay(this); return false;">https</a>') + . '</span>' + . $aIco['type_' . $aCfg['type']] . " $sHost" + . '</td>' + . '<td>' + . ($aCfg['ip'] + ? ($aCfg['ip'] === $sMyIp + ? '<span title="">' . $aIco['ip_ok'] . ' ' . $aCfg['ip'] . '</span>' + : '<span title="Warning: this is not the ip address of the current host (' . $sMyIp . ')">' . $aIco['ip_warn'] . ' ' . $aCfg['ip'] . '</span>' + ) + : '<span class="error">' . $aIco['ip_err'] . ' ERROR: unknown host</span>') + . '</td>' + . '<td>' + . ($aCfg['type'] == "config" + ? '<a href="?cfgfile=redirects_' . $sHost . '.json" onclick="showInOverlay(this); return false;" title="show config for host ' . $sHost . '">' . $aCfg['type'] . '</a> ' + : '<a href="?cfgfile=redirects_' . $aCfg['target'] . '.json" onclick="showInOverlay(this); return false;" title="show config for alias ' . $sHost . ' pointing to host ' . $aCfg['target'] . '">' . $aCfg['type'] . '</a> ' ) . '</td>' - ; - /* - if(!$aCfg['ip']){ - $sErrors.='<li>Host was not found in DNS: '.$sHost.'</li>'; - } - */ - if (isset($aCfg['redirects'])){ - $iCount=0; - foreach(['direct', 'regex'] as $sType){ - if (count($aCfg['redirects'][$sType])){ - foreach($aCfg['redirects'][$sType] as $sFrom=>$aTo){ + ; + if (isset($aCfg['redirects'])) { + $iCount = 0; + foreach (['direct', 'regex'] as $sType) { + if (count($aCfg['redirects'][$sType])) { + foreach ($aCfg['redirects'][$sType] as $sFrom => $aTo) { $iCount++; - $sTable.=$sTdFirst - .'<td class="type-'.$sType.'">'.$sType.'</td>' - .'<td class="type-'.$sType.'">' - . $sFrom - .($sType == 'direct' - ? '<span style="float: right;">' - . '<a href="?url=http://'.$sHost.$sFrom.'" title="click to test http://'.$sHost.'/'.$sFrom.'" onclick="showInOverlay(this); return false;">http</a> ' - . '<a href="?url=https://'.$sHost.$sFrom.'" title="click to test http://'.$sHost.'/'.$sFrom.'" onclick="showInOverlay(this); return false;">https</a> ' - .'</span>' - : '' - ) - .'</td>' - .'<td class="http-'.$aTo['code'].'">'.$aTo['code'].'</td>' - .'<td>'.$aIco['url'].' <a href="?url='.$aTo['target'].'" title="click to test '.$aTo['target'].'" onclick="showInOverlay(this); return false;">'.$aTo['target'].'</a></td>' - .'</tr>'; + $sTable .= $sTdFirst + . '<td class="type-' . $sType . '">' . $sType . '</td>' + . '<td class="type-' . $sType . '">' + . ($sType == 'direct' + ? '<span style="float: right;">' + . '<a href="?url=http://' . $sHost . $sFrom . '" title="click to test http://' . $sHost . '/' . $sFrom . '" onclick="showInOverlay(this); return false;">http</a> ' + . '<a href="?url=https://' . $sHost . $sFrom . '" title="click to test http://' . $sHost . '/' . $sFrom . '" onclick="showInOverlay(this); return false;">https</a> ' + . '</span>' + : '' + ) + . $sFrom + . '</td>' + . '<td class="http-' . $aTo['code'] . '">' . $aTo['code'] . '</td>' + . '<td>' . $aIco['url'] . ' <a href="?url=' . $aTo['target'] . '" title="click to test ' . $aTo['target'] . '" onclick="showInOverlay(this); return false;">' . $aTo['target'] . '</a></td>' + . '</tr>'; } } - + } } else { - // type = alias - // $sHtml.='<tr>'.$sTdFirst.'<td></td><td></td><td></td><td>'.(isset($aCfg['target']) ? 'see config for <a href="#'.getId($aCfg['target']).'">'.$aCfg['target'].'</a>' : '').'</td></tr>'; - $sTable.=$sTdFirst.'<td></td><td></td><td></td><td>'.(isset($aCfg['target']) ? 'see config for <em>'.$aCfg['target'].'</em>' : '').'</td></tr>'; + $sTable .= $sTdFirst . '<td></td><td></td><td></td><td>' + . (isset($aCfg['target']) ? 'see config for <em>' . $aCfg['target'] . '</em>' : '') + . '</td></tr>'; } - + } - $sTable=$sTable + $sTable = $sTable ? '<table class="mydatatable"><thead> <tr> - <th>Host</th> - <th>Ip address</th> - <th>Setup</th> - <th>Type</th> - <th>From</th> - <th>Code</th> - <th>Target</th> + <th>Host</th> + <th>Ip address</th> + <th>Setup</th> + <th>Type</th> + <th>From</th> + <th class="th-code">Code</th> + <th>Target</th> </tr> - </thead><tbody>'.$sTable.'</tbody></table></div>' + </thead><tbody>' . $sTable . '</tbody></table></div>' . '<br><br>' - .'<div class="content legend">' + . '<div class="content legend">' . '<strong>Legend</strong>:<br>' . '<table><tbody>' - .'<tr>' + . '<tr>' . '<td colspan="2"><br>Icons:</td>' - .'</tr>' - .'<tr>' - . '<td>' - . $aIco['type_config'].' Domain with config entries<br>' - . $aIco['type_alias'].' Alias pointng to the config of another domain<br>' - . $aIco['url'].' clickable url to show result of a http head request<br>' - . '</td>' - . '<td>' - . $aIco['ip_ok'].' Domain is on the same ip like the redirect tool.<br>' - . $aIco['ip_warn'].' Domain is on another domain (= the config from here does not work because redirect is handled on another system)<br>' - . $aIco['ip_err'].' Hostname was not found in DNS<br>' - . '</td>' . '</tr>' - .'<tr>' + . '<tr>' + . '<td>' + . $aIco['type_config'] . ' Domain with config entries<br>' + . $aIco['type_alias'] . ' Alias pointng to the config of another domain<br>' + . $aIco['url'] . ' clickable url to show result of a http head request<br>' + . '</td>' + . '<td>' + . $aIco['ip_ok'] . ' Domain is on the same ip like the redirect tool.<br>' + . $aIco['ip_warn'] . ' Domain is on another domain (= the config from here does not work because redirect is handled on another system)<br>' + . $aIco['ip_err'] . ' Hostname was not found in DNS<br>' + . '</td>' + . '</tr>' + . '<tr>' . '<td colspan="2"><br>Redirect status codes:</td>' - .'<tr>' - . '<td colspan="2">' - . '301 Moved Permanently - The address is outdated! However, the new address of the resource is still being forwarded.<br>' - . '307 Temporary Redirect - The URL is currently being redirected temporarily (method is retained) - but the old address remains valid.<br>' - . '308 Permanent Redirect - The address is outdated! The new address of the resource is forwarded with the same method.<br>' - . '</td>' - .'</tr>' - .'</tbody></table>' - .'</div>' - - : '<h3>'.$aIco['welcome'].' Welcome!</h3> + . '<tr>' + . '<td colspan="2">' + . '301 Moved Permanently - The address is outdated! However, the new address of the resource is still being forwarded.<br>' + . '307 Temporary Redirect - The URL is currently being redirected temporarily (method is retained) - but the old address remains valid.<br>' + . '308 Permanent Redirect - The address is outdated! The new address of the resource is forwarded with the same method.<br>' + . '</td>' + . '</tr>' + . '</tbody></table>' + . '</div>' + + : '<h3>' . $aIco['welcome'] . ' Welcome!</h3> <p> Thank you for the installation!<br> Now is a good moment to create your first config. @@ -229,66 +223,61 @@ if (!$oR->isEnabled()){ <li>Relaod this page</li> </ul> <p> - See the <a href="'.$oR->urlDocs.'Configuration.html" target="_blank">Docs</a> for details. + See the <a href="' . $oR->urlDocs . 'Configuration.html" target="_blank">Docs</a> for details. </p> ' - ; + ; - $sHtml.=' - <!-- - <h2>'.$aIco['h2_head'].' Http head tester</h2> - <div class="content"> - <form> - '.$aIco['url'].' <input type="text" name="url" size="100" value="'.$sUrl.'" placeholder="Enter url or click a link in the table below."/> - <button>Http HEAD</button> - </form> - </div> - <br> - --> - <h2>'.$aIco['h2_config'].' Domains and their redirects</h2> - <div class="content">' - - /* - .'<h2>Config array</h2> - <pre>'.print_r($aHosts, 1).'</pre>' - */ - .$sTable - ; + $sHtml .= ' + <h2>' . $aIco['h2_config'] . ' Domains and their redirects</h2> + <div class="content">' + . $sTable + . '</div>' + ; - $sErrors = $sErrors - ? '<!-- <h2>'.$aIco['h2_err'].' Found errors</h2> -->' - // .'<div class="content">' - .'<ol class="error">' - .$sErrors - .'</ol>' - // .'</div>' + $sErrors = $sErrors + ? '<!-- <h2>' . $aIco['h2_err'] . ' Found errors</h2> -->' + . '<ol class="error">' + . $sErrors + . '</ol>' : '' - ; + ; - $sHtml.='<footer><a href="'.$oR->urlRepo.'">Source</a> | <a href="'.$oR->urlDocs.'">Docs</a></footer>' - ; + $sHtml .= '<footer><a href="' . $oR->urlRepo . '">Source</a> | <a href="' . $oR->urlDocs . '">Docs</a></footer>' + ; } // ---------- OUTPUT ?><!doctype html> <html> - <head> - <title>Redirects</title> - <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.11.4/datatables.min.css"/> - <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> - <script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.11.4/datatables.min.js"></script> - <link rel="stylesheet" href="main.css"> - </head> - <body> - <h1><a href="?"> Redirects :: admin</a></h1> +<head> + <title>Redirects</title> + + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" + integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" + crossorigin="anonymous" referrerpolicy="no-referrer"></script> + + <link rel="stylesheet" type="text/css" href="//cdn.datatables.net/2.2.1/css/dataTables.dataTables.min.css" /> + <script type="text/javascript" src="//cdn.datatables.net/2.2.1/js/dataTables.min.js"></script> + + <link rel="stylesheet" href="main.css"> +</head> + +<body> + <h1><a href="?"> Redirects :: admin</a></h1> + + <main> <?php echo $sErrors . $sHtml; ?> - <div id="divoverlay" class="overlay" onclick="this.style.display='none';"></div> - - <script type="text/javascript" src="functions.js"></script> - <script> - - </script> - </body> -</html> + </main> + + <div id="divoverlay" class="overlay" onclick="this.style.display='none';"></div> + + <script type="text/javascript" src="functions.js"></script> + <script> + + </script> +</body> + +</html> \ No newline at end of file diff --git a/public_html/admin/main.css b/public_html/admin/main.css index c63a6adeb7109a42c8237ffd0cabf54139115a6c..d8fd9f535634c3a1a9373040e24213b387d28175 100644 --- a/public_html/admin/main.css +++ b/public_html/admin/main.css @@ -1,8 +1,66 @@ -a{color:royalblue;} +:root{ + + --col-border-1: #6aa; + + --body-bg: linear-gradient(-10deg, #ddd, #fff, #ddd, #e5e5e5) fixed; + --body-color: #335; + + --top-bg: linear-gradient(0deg, #000, #234, #222); + --top-color: #e55; + --top-bottom: var(--col-border-1); + + --link-color: #46d; + + --h2-bg: #c8e0e0; + --h2-color: #458; + --h2-border: var(--col-border-1); + + --h3-color: #ccc; + + --footer-bg: rgba(0,0,0,0.05); + + --box-content-bg: #fff; + --box-legend-bg: #fff4e8; + + --txt-error-bg: #ebb; + --txt-error-color: #800; + --txt-ok-bg: #aeb; + --txt-ok-color: #080; + --txt-warning-bg: #fda; + --txt-warning-color: #651; + + --txt-alias-color: #89a; + + --http-301-color: #a55; + --http-307-color: #488; + --http-308-color: #a95; + + --type-direct-color:#383; + --type-regex-color:#838; + + --txt-statuscode-bg: rgba(255,255,255,0.8); + + --txt-location-bg: #cde; + --txt-location-color: #236; + + --txt-location-before-bg: #fff; + --txt-debug-color: #197; + + --overlay-bg: rgba(0,0,0,0.3); + --overlay-box-bg: #f8f8f8; + --overlay-box-shadow: 0 0 3em #000; + + --spin-color1: #ccc; + --spin-color2: var(--col-border-1); + +} + + +a{color: var(--link-color);} body { - background: linear-gradient(-10deg, #ddd, #fff, #ddd, #e5e5e5) fixed; - color: #335; + background: var(--body-bg); + color: var(--body-color); font-size: 1.2em; font-family: arial; margin: 0; @@ -10,49 +68,54 @@ body { height: 100%,; } -h1{margin: 0 0 1em;; padding: 0;} -h1 a{background: linear-gradient(0deg, #000, #234, #222); color: #e55; text-decoration: none;border-bottom: 5px solid #6aa; display: block; padding: 0.5em;} +h1{margin: 0 0 0.5em;; padding: 0;} +h1 a{background: var(--top-bg); color: var(--top-color); text-decoration: none;border-bottom: 5px solid var(--top-bottom); display: block; padding: 0.5em;} + +h2{background: var(--h2-bg); color: var('--h2-color'); margin: 1em 0 0.5em; border-top: 2px solid var(--h2-border); border-left: 5px solid var(--h2-border); border-top-left-radius: 0.5em; padding: 0.5em; margin: 0;} +h3{color:var('--h3-color'); font-size: 250%} + +pre{background: rgba(0,0,0,0.02);padding: 0.3em 1em; border: 1px solid rgba(0,0,0,0.1); margin: 2em 0 3em;; border-bottom: 2px solid rgba(0,0,0,0.2); border-bottom-right-radius: 1em;} -h2{background: #dee; color:#458; margin: 1em 0 0.5em; border-top: 2px solid #fff; border-left: 5px solid #fff; border-top-left-radius: 0.5em; padding: 0.5em; margin: 0 0 1em;} -h3{color:#ccc; font-size: 250%} +.dataTable tr:hover{background: #f4f0f8 !important;} +footer{background: var(--footer-bg); margin-top: 4em; text-align: right; padding: 1em;} +main{margin: 0 2em; padding: 0em;} -pre{background: rgba(0,0,0,0.02);padding: 0.3em 1em; border: 1px solid rgba(0,0,0,0.1); margin: 2em 0 3em;; border-bottom: 2px solid rgba(0,0,0,0.2);} +.content{background: var(--box-content-bg); padding: 1em;} +.legend{background: var(--box-legend-bg); border-top: 1px dashed; padding: 1em;} +.error{background: var(--txt-error-bg); color: var(--txt-error-color); padding: 0.2em 0.5em;} +.warning{background: var(--txt-warning-bg); color: var(--txt-warning-color); } -tr:hover{background: #f4f0f8 !important;} -footer{background:rgba(0,0,0,0.03); margin-top: 4em; text-align: right;padding: 1em;} +ol.error{padding-left: 2em;} -.content{margin: 0 1em; background: #fff; margin: 0 3em; padding: 1em;} -.legend{background: none; border: 1px dashed; padding: 1em;} -.error{background: #fbb; padding: 0.2em 1.5em;} -.warning{color:#651; background:#fec; padding: 0.2em 1em;} +.th-code{min-width: 12em;} -.cfgtype-alias{color:#89a;} -.http-301::after{color:#a55; content: ' (Moved Permanently)'} -.http-307::after{color:#488; content: ' (Temporary Redirect)'} -.http-308::after{color:#a95; content: ' (Permanent Redirect)'} -.type-direct{color:#383; } -.type-regex{color:#838; } +.cfgtype-alias{color:var(--txt-alias-color);} +.http-301::after{color: var(--http-301-color); content: ' (Moved Permanently)'} +.http-307::after{color:var(--http-307-color); content: ' (Temporary Redirect)'} +.http-308::after{color:var(--http-308-color); content: ' (Permanent Redirect)'} +.type-direct{color: var(--type-direct-color); } +.type-regex{color: var(--type-regex-color); } -.status{padding: 0.5em 1em; position: relative;top: -0.8em; border-left: 2px solid; border-left_: 1.5em solid;font-size: 125%;} -.status-ok{color:#080; background:#cec; } -.status-redirect{color:#651; background:#fec;} -.status-error{color:#800; background:#ecc;} -.statuscode{background: rgba(255,255,255,0.8); font-size: 150%;} +.status{padding: 0.5em 1em; position: relative; top: -1.4em; left: -0.9em; border-left: 5px solid; font-size: 125%; border-bottom-right-radius: 0.7em;} +.status-ok{background: var(--txt-ok-bg); color: var(--txt-ok-color);} +.status-redirect{background: var(--txt-warning-bg); color: var(--txt-warning-color);} +.status-error{background: var(--txt-error-bg); color: var(--txt-error-color);} +.statuscode{background: var(--txt-statuscode-bg); font-size: 150%;} -.location{background:#cde; border: 1px solid rgba(0,0,0,0.2); font-size: 125%; color:#236;} -.location::before{content:' > '; color:#800; background-color: #fff;} -.debug{color:#197;} +.location{background:var(--txt-location-bg); font-size: 125%; color:var(--txt-location-color);} +.location::before{content:' 🌐 '; background-color: var(--txt-location-before-bg);} +.debug{color:var(--txt-debug-color);} -.overlay{position: fixed; margin: 0; width: 100%; height: 100%; top: 0; left: 0; background: rgba(0,0,0,0.3);overflow: scroll; display: none;} -.overlay>div{margin: 3% 10%; background: #f8f8f8; padding: 1em;box-shadow: 0 0 3em #000; } +.overlay{position: fixed; margin: 0; width: 100%; height: 100%; top: 0; left: 0; background: var(--overlay-bg); overflow: scroll; display: none;} +.overlay>div{margin: 3% 10%; background: var(--overlay-box-bg); padding: 1em;box-shadow: var(--overlay-box-shadow); } .spin { display: inline-block; width: 50px; height: 50px; - border: 3px solid #ccc; + border: 3px solid var(--spin-color1); border-radius: 50%; - border-top-color: #1c87c9; + border-top-color: var(--spin-color2); animation: spin 1s ease-in-out infinite; -webkit-animation: spin 1s ease-in-out infinite; } diff --git a/public_html/classes/redirect.admin.class.php b/public_html/classes/redirect.admin.class.php index 9a1ed976c69ddc31599ab96b986100258b15b1fd..30a8f613e930c76d2d94b7c172a93d6fd734a306 100644 --- a/public_html/classes/redirect.admin.class.php +++ b/public_html/classes/redirect.admin.class.php @@ -57,13 +57,15 @@ class redirectadmin extends redirect } /** - * Make a single http(s) get request and return the response body + * Make a single http(s) get request and return an array with response header, body, curl error info * @param string $url url to fetch * @param boolean $bHeaderOnly optional: true=make HEAD request; default: false (=GET) - * @return string + * @return array */ - public function httpGet(string $url, bool $bHeaderOnly = false): bool|string + public function httpGet(string $url, bool $bHeaderOnly = false): array { + $aResult = []; + $ch = curl_init($url); foreach ($this->_getCurlOptions() as $sCurlOption => $sCurlValue) { curl_setopt($ch, $sCurlOption, $sCurlValue); @@ -73,29 +75,46 @@ class redirectadmin extends redirect curl_setopt($ch, CURLOPT_NOBODY, 1); } $res = curl_exec($ch); + + $sHeader = ''; + $sBody = ''; + $aResponse = explode("\r\n\r\n", $res, 2); + list($sHeader, $sBody) = count($aResponse) > 1 + ? $aResponse + : [$aResponse[0], '']; + + $aResult = [ + 'url' => $url, + 'response_header' => $sHeader, + 'response_body' => $sBody, + 'curlinfo' => curl_getinfo($ch), + 'curlerrorcode' => curl_errno($ch), + 'curlerrormsg' => curl_error($ch), + ]; + curl_close($ch); - return ($res); + return $aResult; } /** * Get html code for a response header of a request * - * @param string $sHeader + * @param array $aResponse response array from httpGet() method * @return string */ - public function renderHttpResponseHeader(string $sHeader): string + public function renderHttpResponseHeader(array $aResponse): string { + $sHeader=$aResponse['response_header']; $sReturn = $sHeader; - if (!$sReturn) { - $sReturn = '<pre><span class="status status-error">Request failed. </span><br>' - . 'No data... no response.<br>' - . 'Maybe ... ' - . '<ul>' - . '<li>the nostname does not exist ... </li>' - . '<li>or there is a network problem ... or </li>' - . '<li>the webservice on the target system does not run.</li>' - . '</ul>' - . '</pre>' + // $sReturn.="<pre>".print_r($aResponse, 1)."</pre>"; + + if ($aResponse['curlerrorcode']) { + $sReturn = '<pre><br>' + .'<span class="status status-error">Request failed.<br></span>' + .'Curl error #'.$aResponse['curlerrorcode'] .':<br>' + . '<strong>'.$aResponse['curlerrormsg'].'</strong><br><br>' + .'</pre>' + .'🌐 <a href="https://curl.se/libcurl/c/libcurl-errors.html" target="_blank">Curl error codes</a>' ; } else {