File modules/cms/status/Status.class.php

Last commit: Thu Feb 16 01:04:38 2023 +0100	Jan Dankert	New: Tags for base objects.
1 <?php 2 3 namespace cms\status; 4 5 use BadMethodCallException; 6 use cms\action\RequestParams; 7 use cms\base\Configuration; 8 use cms\base\DB; 9 use cms\base\DefaultConfig; 10 use cms\base\Startup; 11 use cms\Dispatcher; 12 use cms\update\Update; 13 use configuration\Config; 14 use configuration\ConfigurationLoader; 15 use database\Database; 16 use Exception; 17 use util\Http; 18 use logger\Logger; 19 use \util\exception\ObjectNotFoundException; 20 use util\exception\UIException; 21 use util\exception\SecurityException; 22 use util\json\JSON; 23 use util\Session; 24 use util\XML; 25 use util\YAML; 26 27 /** 28 * Actuator. 29 */ 30 class Status 31 { 32 /** 33 * Getting the state 34 */ 35 public static function execute() 36 { 37 $cmd = $_SERVER['QUERY_STRING']; 38 $data = []; 39 $success = false; 40 41 switch( $cmd ) { 42 case '': 43 case 'health': 44 case 'status': 45 case 'state': 46 47 $data['state'] = 'UP'; 48 $success = true; 49 break; 50 51 case 'db': 52 case 'database': 53 54 $config = new Config( self::getConfiguration() ); 55 $databases = []; 56 foreach( $config->subset('database')->subsets() as $dbName => $dbConfig ) { 57 58 if (!$dbConfig->is('enabled', true)) { 59 $dbState = [ 60 'state' => 'DISABLED', 61 ]; 62 63 } else { 64 65 try { 66 $db = new Database($dbConfig->subset('read')->getConfig() + $dbConfig->getConfig()); 67 68 $update = new Update(); 69 70 if ( ! $update->isUpdateRequired( $db ) ) 71 $dbState = [ 72 'state' => 'UP', 73 ]; 74 else 75 $dbState = [ 76 'state' => 'DOWN', 77 'message' => 'NEEDS UPGRADE', 78 ]; 79 80 $db->disconnect(); 81 82 } catch (\Exception $e) { 83 $dbState = [ 84 'state' => 'DOWN', 85 'message' => 'The database is not available', 86 'cause' => Status::getCause($e), 87 ]; 88 } 89 90 } 91 92 $databases[$dbName] = $dbState; 93 } 94 95 $data['databases'] = $databases; 96 $success = true; 97 break; 98 99 case 'upgrade': 100 case 'update': 101 case 'install': 102 103 $config = new Config( self::getConfiguration() ); 104 $databases = []; 105 foreach( $config->subset('database')->subsets() as $dbName => $dbConfig ) { 106 107 $dbState = []; 108 109 if (!$dbConfig->is('enabled', true)) { 110 $dbState = [ 111 'state' => 'DISABLED', 112 ]; 113 114 } else { 115 116 try { 117 $adminDb = new Database($dbConfig->subset('admin')->getConfig() + $dbConfig->getConfig()); 118 $adminDb->id = $dbName; 119 120 $updater = new Update(); 121 122 if ( ! $updater->isUpdateRequired( $adminDb ) ) { 123 $dbState = [ 124 'state' => 'UP', 125 ]; 126 } 127 else { 128 129 if (!$dbConfig->is('auto_update', true)) 130 $dbState = [ 131 'state' => 'DOWN', 132 'message' => 'DB Update required, but auto-update is disabled. ' . Startup::TITLE . " " . Startup::VERSION . " needs DB-version " . Update::SUPPORTED_VERSION 133 ]; 134 else { 135 $updater->update($adminDb); 136 137 // Try to close the PDO connection. PDO doc: 138 // To close the connection, you need to destroy the object by ensuring that all 139 // remaining references to it are deleted—you do this by assigning NULL to the variable that holds the object. 140 // If you don't do this explicitly, PHP will automatically close the connection when your script ends. 141 $adminDb = null; 142 unset($adminDb); 143 144 $dbState = [ 145 'state' => 'UP', 146 'message' => 'Updated', 147 ]; 148 } 149 } 150 151 152 } catch (\Exception $e) { 153 $dbState = [ 154 'state' => 'DOWN', 155 'message' => 'Database is not available', 156 'cause' => Status::getCause($e), 157 ]; 158 } 159 160 } 161 162 $databases[$dbName] = $dbState; 163 } 164 165 $data['databases'] = $databases; 166 $success = true; 167 break; 168 169 case 'info': 170 $data = [ 171 'version' => Startup::VERSION, 172 'date' => Startup::DATE, 173 'name' => Startup::TITLE, 174 'api' => Startup::API_LEVEL, 175 ]; 176 $success = true; 177 break; 178 179 case 'system': 180 $data = [ 181 'interpreter' => PHP_VERSION, 182 'os' => PHP_OS, 183 'usage' => getrusage(), 184 'memory' => [ 185 'allocated' => memory_get_usage(true), 186 'used' => memory_get_usage(), 187 'peak_allocated' => memory_get_peak_usage(true), 188 'peak_used' => memory_get_peak_usage(), 189 190 ], 191 ]; 192 $success = true; 193 break; 194 195 case 'env': 196 case 'environment': 197 if ( version_compare(PHP_VERSION,'7.1','>=') ) 198 $data['environment'] = getenv(); // since PHP 7.1 199 $success = true; 200 break; 201 202 case 'server': 203 $data['server'] = $_SERVER; 204 $success = true; 205 break; 206 207 case 'extensions': 208 $data['extensions'] = get_loaded_extensions(); 209 $success = true; 210 break; 211 212 case 'config': 213 case 'configuration': 214 215 $data['configuration'] = self::getConfiguration(); 216 $success = true; 217 218 break; 219 220 default: 221 } 222 223 // Be safe! We must clear secret values. 224 array_walk_recursive($data, function(&$value,$key) { 225 if ( stripos($key,'secret' ) !== FALSE || 226 stripos($key,'password') !== FALSE || 227 stripos($key,'pass' ) !== FALSE ) 228 $value = '**********'; 229 }); 230 231 232 header('Content-Type: application/json; charset=UTF-8'); 233 $output = JSON::encode($data); 234 235 if (!headers_sent()) { 236 // HTTP Spec: 237 // "Applications SHOULD use this field to indicate the transfer-length of the 238 // message-body, unless this is prohibited by the rules in section 4.4." 239 // 240 // And the overhead of 'Transfer-Encoding: chunked' is eliminated... 241 header('HTTP/1.0 ' . ($success ? '200 OK' : '503 Internal Server Error') ); 242 header('Content-Length: ' . strlen($output)); 243 244 } 245 246 echo $output; 247 } 248 249 250 private static function getConfiguration() { 251 $configFile = getenv( 'CMS_CONFIG_FILE' ); 252 if ( ! $configFile ) 253 $configFile = Startup::DEFAULT_CONFIG_FILE; 254 255 $configLoader = new ConfigurationLoader( $configFile ); 256 return $configLoader->load(); 257 } 258 259 private static function getCause(Exception $e) 260 { 261 return 262 [ 263 'class' => get_class($e), 264 'message' => $e->getMessage(), 265 'line' => $e->getLine(), 266 'file' => $e->getFile(), 267 'code' => $e->getCode(), 268 ] + (($e->getPrevious())?['cause'=>Status::getCause($e->getPrevious())]:[]); 269 } 270 }
Download modules/cms/status/Status.class.php
History Thu, 16 Feb 2023 01:04:38 +0100 Jan Dankert New: Tags for base objects. Sun, 4 Dec 2022 19:34:11 +0100 Jan Dankert Fix: Detecting a upgrade was broken; Show full trace in error messages. Sun, 7 Nov 2021 23:45:50 +0100 Jan Dankert Fix: First successful migration to the new "content" table. Sun, 7 Nov 2021 21:43:45 +0100 Jan Dankert New: 2 new status pages: system, server. Sat, 6 Nov 2021 01:35:17 +0100 Jan Dankert Cleanup Sat, 6 Nov 2021 00:01:59 +0100 Jan Dankert New: Status interface for health checks.