diff --git a/.devilbox/www/config.php b/.devilbox/www/config.php index af763501..964dd730 100644 --- a/.devilbox/www/config.php +++ b/.devilbox/www/config.php @@ -36,9 +36,12 @@ $HTTPD_HOST_ADDR = gethostbyname($HTTPD_HOST_NAME); $PHP_HOST_NAME = 'php'; $PHP_HOST_ADDR = gethostbyname($PHP_HOST_NAME); -$MYSQL_HOST_NAME = 'db'; +$MYSQL_HOST_NAME = 'mysql'; $MYSQL_HOST_ADDR = gethostbyname($MYSQL_HOST_NAME); +$POSTGRES_HOST_NAME = 'postgres'; +$POSTGRES_HOST_ADDR = gethostbyname($POSTGRES_HOST_NAME); + // // Load files @@ -46,19 +49,20 @@ $MYSQL_HOST_ADDR = gethostbyname($MYSQL_HOST_NAME); require $LIB_DIR . DIRECTORY_SEPARATOR . 'Logger.php'; require $LIB_DIR . DIRECTORY_SEPARATOR . 'Docker.php'; require $LIB_DIR . DIRECTORY_SEPARATOR . 'Mysql.php'; +require $LIB_DIR . DIRECTORY_SEPARATOR . 'Postgres.php'; // // Instantiate Basics // -$Logger = \devilbox\Logger::getInstance(); -$Docker = \devilbox\Docker::getInstance(); -$MySQL = \devilbox\Mysql::getInstance('root', $Docker->getEnv('MYSQL_ROOT_PASSWORD'), $MYSQL_HOST_ADDR); - +$Logger = \devilbox\Logger::getInstance(); +$Docker = \devilbox\Docker::getInstance(); +$MySQL = \devilbox\Mysql::getInstance('root', $Docker->getEnv('MYSQL_ROOT_PASSWORD'), $MYSQL_HOST_ADDR); +$Postgres = \devilbox\Postgres::getInstance($Docker->getEnv('POSTGRES_USER'), $Docker->getEnv('POSTGRES_PASSWORD'), $POSTGRES_HOST_ADDR); // VirtualHost DNS check // Temporarily disable due to: // https://github.com/cytopia/devilbox/issues/8 -$ENABLE_VHOST_DNS_CHECK = false; +$ENABLE_VHOST_DNS_CHECK = true; diff --git a/.devilbox/www/include/footer.php b/.devilbox/www/include/footer.php index c6285556..9d151e59 100644 --- a/.devilbox/www/include/footer.php +++ b/.devilbox/www/include/footer.php @@ -9,6 +9,7 @@ + diff --git a/.devilbox/www/include/lib/Docker.php b/.devilbox/www/include/lib/Docker.php index cf2e989d..4e5526eb 100644 --- a/.devilbox/www/include/lib/Docker.php +++ b/.devilbox/www/include/lib/Docker.php @@ -108,7 +108,7 @@ class Docker { if (!isset($this->_env[$variable])) { $Logger = \devilbox\Logger::getInstance(); - $logger->error('Docker environment variable not found: '.$variable); + $Logger->error('Docker environment variable not found: '.$variable); return null; } return $this->_env[$variable]; @@ -296,7 +296,7 @@ class Docker $version = $this->MySQL_config('version'); if (!$name && !$version) { - return 'Unknown Version'; + return 'Unknown MySQL version'; } return $name . ' ' . $version; } @@ -342,6 +342,74 @@ class Docker } + /********************************************************************************* + * + * Postgres Docker functions + * + *********************************************************************************/ + + /** + * Get Postgres Version. + * + * @return string Postgres version string. + */ + public function Postgres_version() + { + $callback = function ($row, &$data) { + $data = $row['version']; + }; + + $version = \devilbox\Postgres::getInstance()->select('SELECT version();', $callback); + + // Extract shorthand + preg_match('/\w+[[:space:]]*[.0-9]+/i', $version, $matches); + if (isset($matches[0])) { + return $matches[0]; + } + + // Unknown + if (!$version) { + return 'Unknown Postgres version'; + } + return $version; + } + + + /** + * Read out PostgreSQL Server configuration by variable + * + * @param string|null $key Config key name + * @return string|mixed[] + */ + public function Postgres_config($key = null) + { + // Get all configs as array + if ($key === null) { + $callback = function ($row, &$data) { + $key = $row['name']; + $val = $row['setting']; + $data[$key] = $val; + }; + + $sql = 'SELECT name, setting FROM pg_settings;'; + $configs = \devilbox\Postgres::getInstance()->select($sql, $callback); + + return $configs ? $configs : array(); + + } else { // Get single config + + $callback = function ($row, &$data) use ($key) { + $data = isset($row['setting']) ? $row['setting'] : false; + }; + + $sql = "SELECT name, setting FROM pg_settings WHERE name = '".$key."';"; + $val = \devilbox\Postgres::getInstance()->select($sql, $callback); + + return is_array($val) ? '' : $val; + } + } + + /********************************************************************************* * diff --git a/.devilbox/www/include/lib/Postgres.php b/.devilbox/www/include/lib/Postgres.php new file mode 100644 index 00000000..dbd4972f --- /dev/null +++ b/.devilbox/www/include/lib/Postgres.php @@ -0,0 +1,348 @@ +getConnectError())) { + \devilbox\Logger::getInstance()->error('Instance has errors:' . "\r\n" . var_export(static::$instance, true) . "\r\n"); + //return null; + } + return static::$instance; + } + + /** + * Connect to database + * + * @param string $err Reference to error message + * @param string $user Postgres username + * @param string $pass Postgres password + * @param string $host Postgres hostname + * @return boolean + */ + public static function testConnection(&$err, $user, $pass, $host) + { + $err = false; + + // Silence errors and try to connect + error_reporting(0); + $link = pg_pconnect('host='.$host.' user='.$user.' password='.$pass); + error_reporting(-1); + + if (!$link || pg_connection_status($link) !== PGSQL_CONNECTION_OK) { + $err = 'Failed to connect: ' .pg_last_error($link); + return false; + } + pg_close($link); + return true; + } + + + + /********************************************************************************* + * + * Private Variables + * + *********************************************************************************/ + + /** + * Postgres Resource + * @var resource|null + */ + private $_link = null; + + /** + * Connection error string + * @var string + */ + private $_connect_error = ''; + + /** + * Connection error code + * @var integer + */ + private $_connect_errno = 0; + + /** + * Error string + * @var string + */ + private $_error = ''; + + + /** + * Error code + * @var integer + */ + private $_errno = 0; + + + + /********************************************************************************* + * + * Construct/Destructor + * + *********************************************************************************/ + + /** + * Use singleton getInstance() instead. + * + * @param string $user Username + * @param string $pass Password + * @param string $host Host + * @param string $database Database name + */ + public function __construct($user, $pass, $host, $database = null) + { + // Silence errors and try to connect + error_reporting(0); + if ($database !== null) { + $link = pg_pconnect('host='.$host.' dbname='.$database.' user='.$user.' password='.$pass); + } else { + $link = pg_pconnect('host='.$host.' user='.$user.' password='.$pass); + } + error_reporting(-1); + + if (!$link || pg_connection_status($link) !== PGSQL_CONNECTION_OK) { + $this->_connect_error = 'Failed to connect to '.$user.'@'.$host; + $this->_connect_errno = 1; + \devilbox\Logger::getInstance()->error($this->_connect_error); + } else { + $this->_link = $link; + } + } + + + + + + /** + * Destructor + */ + public function __destruct() + { + if ($this->_link) { + pg_close($this->_link); + } + } + + /********************************************************************************* + * + * PostgreSQL Select functions + * + *********************************************************************************/ + + /** + * Query Database + * + * @param string $query Postgres Query + * @param function $callback Callback function + * @return mixed[] + */ + public function select($query, $callback = null) + { + if (!$this->_link) { + \devilbox\Logger::getInstance()->error('Postgres error, link is no resource in select()'); + return false; + } + + if (!($result = pg_query($this->_link, $query))) { + $this->_error = 'PostgreSQL - error on result: '.pg_result_error($result)."\n" . 'query:'."\n" . $query; + $this->_errno = 1; + \devilbox\Logger::getInstance()->error($this->_error); + return false; + } + + $data = array(); + + if ($callback) { + while ($row = pg_fetch_assoc($result)) { + $callback($row, $data); + } + } else { + while ($row = pg_fetch_assoc($result)) { + $data[] = $row; + } + } + pg_free_result($result); + + return $data; + } + + /** + * Get all PostgreSQL Databases. + * @return mixed[] Array of databases + */ + public function getDatabases() + { + $callback = function ($row, &$data) { + $data[$row['database']] = array( + 'charset' => $row['charset'], + 'collation' => $row['collation'] + ); + }; + + $sql = 'SELECT + S.datname AS database, + S.datcollate AS collation, + pg_encoding_to_char(S.encoding) AS charset + FROM + pg_database AS S + WHERE datistemplate = false;'; + + $databases = $this->select($sql, $callback); + $databases = $databases ? $databases : array(); + + // Get schemas for each database + foreach ($databases as $name => &$database) { + $PSQL = new Postgres('postgres', \devilbox\Docker::getInstance()->getEnv('POSTGRES_PASSWORD'), $GLOBALS['POSTGRES_HOST_ADDR'], $name); + + $sql = "SELECT n.nspname AS schemas FROM pg_catalog.pg_namespace AS n WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema';"; + $callback = function ($row, &$data) { + $data[$row['schemas']] = array(); + }; + $schemas = $PSQL->select($sql, $callback); + $databases[$name]['schemas'] = $schemas; + } + + return $databases; + } + + + /** + * Get Schema size in Megabytes. + * + * @param string $database Database name. + * @param string $schema Schema name. + * @return integer + */ + public function getSchemaSize($database, $schema) + { + $PSQL = new Postgres('postgres', \devilbox\Docker::getInstance()->getEnv('POSTGRES_PASSWORD'), $GLOBALS['POSTGRES_HOST_ADDR'], $database); + $callback = function ($row, &$data) { + $data = $row['size']; + + }; + $sql = "SELECT + ROUND(sum(table_size) / 1048576, 2) AS size + FROM ( + SELECT pg_catalog.pg_namespace.nspname AS schema_name, + pg_relation_size(pg_catalog.pg_class.oid) AS table_size + FROM pg_catalog.pg_class + JOIN pg_catalog.pg_namespace ON relnamespace = pg_catalog.pg_namespace.oid + WHERE pg_catalog.pg_namespace.nspname = '".$schema."' + ) t + GROUP BY schema_name;"; + + $size = $PSQL->select($sql, $callback); + return $size ? $size : 0; + } + + /** + * Get Number of Tables per Schema + * + * @param string $database Database name. + * @param string $schema Schema name. + * @return integer + */ + public function getTableCount($database, $schema) + { + $PSQL = new Postgres('postgres', \devilbox\Docker::getInstance()->getEnv('POSTGRES_PASSWORD'), $GLOBALS['POSTGRES_HOST_ADDR'], $database); + $callback = function ($row, &$data) { + $data = $row['count']; + }; + + $sql = "SELECT + COUNT(*) AS count + FROM + information_schema.tables + WHERE + table_schema = '".$schema."' + AND + table_type = 'BASE TABLE'; + "; + + $count = $PSQL->select($sql, $callback); + return $count ? $count : 0; + } + + + + + + + /********************************************************************************* + * + * MySQL Error functions + * + *********************************************************************************/ + + /** + * Return connection error message. + * + * @return string Error message + */ + public function getConnectError() + { + return $this->_connect_error; + } + + /** + * Return connection errno code. + * + * @return integer Error code + */ + public function getConnectErrno() + { + return $this->_connect_errno; + } + + /** + * Return error message. + * + * @return string Error message + */ + public function getError() + { + return $this->_error; + } + + /** + * Return errno code. + * + * @return integer Error code + */ + public function getErrno() + { + return $this->_errno; + } +} diff --git a/.devilbox/www/include/navigation.php b/.devilbox/www/include/navbar.php similarity index 51% rename from .devilbox/www/include/navigation.php rename to .devilbox/www/include/navbar.php index 5ad7cfb5..a3315edb 100644 --- a/.devilbox/www/include/navigation.php +++ b/.devilbox/www/include/navbar.php @@ -2,7 +2,7 @@ -
\ No newline at end of file +