More work on web installer
This commit is contained in:
parent
9fed113f74
commit
f5b4928538
23 changed files with 728 additions and 186 deletions
|
@ -1,20 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User: sourcefabric
|
||||
* Date: 02/12/14
|
||||
*
|
||||
* Class DatabaseSetup
|
||||
*
|
||||
* Wrapper class for validating and installing the Airtime database during the installation process
|
||||
*/
|
||||
class DatabaseSetup extends Setup {
|
||||
|
||||
// airtime.conf section header
|
||||
const SECTION = "[database]";
|
||||
|
||||
// Constant form field names for passing errors back to the front-end
|
||||
const DB_USER = "dbUser",
|
||||
DB_PASS = "dbPass",
|
||||
DB_NAME = "dbName",
|
||||
DB_HOST = "dbHost";
|
||||
|
||||
// Form field values
|
||||
static $user, $pass, $name, $host;
|
||||
|
||||
// Array of key->value pairs for airtime.conf
|
||||
static $properties;
|
||||
|
||||
// Message and error fields to return to the front-end
|
||||
static $message = null;
|
||||
static $errors = array();
|
||||
|
||||
|
@ -23,6 +32,13 @@ class DatabaseSetup extends Setup {
|
|||
self::$pass = $settings[self::DB_PASS];
|
||||
self::$name = $settings[self::DB_NAME];
|
||||
self::$host = $settings[self::DB_HOST];
|
||||
|
||||
self::$properties = array(
|
||||
"host" => self::$host,
|
||||
"dbname" => self::$name,
|
||||
"dbuser" => self::$user,
|
||||
"dbpass" => self::$pass,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,47 +49,70 @@ class DatabaseSetup extends Setup {
|
|||
*/
|
||||
function runSetup() {
|
||||
// Check the connection and user credentials
|
||||
if ($this->validateDatabaseConnection()) {
|
||||
if ($this->checkDatabaseConnection()) {
|
||||
// We know that the user credentials check out, so check if the database exists
|
||||
if ($this->validateDatabaseSettings()) {
|
||||
// The database already exists, so we can just set up the schema
|
||||
if ($this->createDatabaseTables()) {
|
||||
self::$message = "Successfully installed Airtime database to '" . self::$name . "'";
|
||||
if ($this->checkDatabaseExists()) {
|
||||
// The database already exists, check if the schema exists as well
|
||||
if ($this->checkSchemaExists()) {
|
||||
self::$message = "Airtime is already installed in this database!";
|
||||
} else {
|
||||
self::$message = "Something went wrong setting up the Airtime schema!";
|
||||
if ($this->createDatabaseTables()) {
|
||||
self::$message = "Successfully installed Airtime database to '" . self::$name . "'";
|
||||
} else {
|
||||
self::$message = "Something went wrong setting up the Airtime schema!";
|
||||
self::$errors[] = self::DB_NAME;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The database doesn't exist, so check if the user can create databases
|
||||
if ($this->checkUserCanCreateDb()) {
|
||||
// The user can create a database, do it
|
||||
if ($this->createDatabase()) {
|
||||
if ($this->createDatabaseTables()) {
|
||||
self::$message = "Successfully installed Airtime database to '" . self::$name . "'";
|
||||
// Ensure that the database was installed in UTF8 (we only care about the Airtime database)
|
||||
if ($this->checkDatabaseSchema()) {
|
||||
if ($this->createDatabaseTables()) {
|
||||
self::$message = "Successfully installed Airtime database to '" . self::$name . "'";
|
||||
} else {
|
||||
self::$message = "Something went wrong setting up the Airtime schema!";
|
||||
self::$errors[] = self::DB_NAME;
|
||||
}
|
||||
} else {
|
||||
self::$message = "Something went wrong setting up the Airtime schema!";
|
||||
self::$message = "The database was installed with an incorrect encoding type!";
|
||||
self::$errors[] = self::DB_NAME;
|
||||
}
|
||||
} else {
|
||||
self::$message = "There was an error installing the database!";
|
||||
self::$errors[] = self::DB_NAME;
|
||||
}
|
||||
} // The user can't create databases, so we're done
|
||||
else {
|
||||
self::$message = "No database " . self::$name . " exists; user " . self::$user
|
||||
. " does not have permission to create databases on " . self::$host;
|
||||
self::$errors[] = self::DB_NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count(self::$errors) <= 0) {
|
||||
$this->writeToTemp();
|
||||
}
|
||||
|
||||
return array(
|
||||
"message" => self::$message,
|
||||
"errors" => self::$errors,
|
||||
);
|
||||
}
|
||||
|
||||
function writeToTemp() {
|
||||
parent::writeToTemp(self::SECTION, self::$properties);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user's database connection is valid
|
||||
* @return boolean true if the connection are valid
|
||||
*/
|
||||
function validateDatabaseConnection() {
|
||||
function checkDatabaseConnection() {
|
||||
// This is pretty redundant, but we need to test both
|
||||
// the existence and the validity of the given credentials
|
||||
exec("export PGPASSWORD=" . self::$pass . " && psql -h "
|
||||
|
@ -97,12 +136,23 @@ class DatabaseSetup extends Setup {
|
|||
* Check if the database settings and credentials given are valid
|
||||
* @return boolean true if the database given exists and the user is valid and can access it
|
||||
*/
|
||||
function validateDatabaseSettings() {
|
||||
function checkDatabaseExists() {
|
||||
exec("export PGPASSWORD=" . self::$pass . " && psql -lqt -h " . self::$host . " -U " . self::$user
|
||||
. "| cut -d \\| -f 1 | grep -w " . self::$name, $out, $status);
|
||||
return $status == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database schema has already been set up
|
||||
* @return boolean true if the database schema exists
|
||||
*/
|
||||
function checkSchemaExists() {
|
||||
// Check for cc_pref to see if the schema is already installed in this database
|
||||
exec("export PGPASSWORD=" . self::$pass . " && psql -U " . self::$user . " -h "
|
||||
. self::$host . " -d " . self::$name . " -tAc \"SELECT * FROM cc_pref\"", $out, $status);
|
||||
return $status == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given user has access on the given host to create a new database
|
||||
* @return boolean true if the given user has permission to create a database on the given host
|
||||
|
@ -132,15 +182,33 @@ class DatabaseSetup extends Setup {
|
|||
$sqlDir = dirname(dirname(__DIR__)) . "/build/sql/";
|
||||
$files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql");
|
||||
|
||||
foreach($files as $f) {
|
||||
foreach ($files as $f) {
|
||||
try {
|
||||
exec("export PGPASSWORD=" . self::$pass . " && psql -U " . self::$user . " --dbname "
|
||||
. self::$name . " -h " . self::$host . " -f $sqlDir$f 2>/dev/null", $out, $status);
|
||||
} catch(Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the newly-created database's encoding was properly set to UTF8
|
||||
* @return boolean true if the database encoding is UTF8
|
||||
*/
|
||||
function checkDatabaseEncoding() {
|
||||
exec("export PGPASSWORD=" . self::$pass . " && psql -U " . self::$user . " -h "
|
||||
. self::$host . " -d " . self::$name . " -tAc \"SHOW SERVER_ENCODING\"", $out, $status);
|
||||
return $out && $out[0] == "UTF8";
|
||||
}
|
||||
|
||||
// TODO Since we already check the encoding, is there a purpose to verifying the schema?
|
||||
function checkDatabaseSchema() {
|
||||
$outFile = "/tmp/tempSchema.xml";
|
||||
exec("export PGPASSWORD=" . self::$pass . " && psql -U " . self::$user . " -h "
|
||||
. self::$host . " -o ${outFile} -tAc \"SELECT database_to_xml(FALSE, FALSE, '"
|
||||
. self::$name . "')\"", $out, $status);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
31
airtime_mvc/public/setup/finish-setup.php
Normal file
31
airtime_mvc/public/setup/finish-setup.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User: sourcefabric
|
||||
* Date: 09/12/14
|
||||
*
|
||||
* Class FinishSetup
|
||||
*
|
||||
* Wrapper class for finalizing and moving airtime.conf
|
||||
*/
|
||||
class FinishSetup extends Setup {
|
||||
|
||||
function __construct($settings) {
|
||||
}
|
||||
|
||||
function runSetup() {
|
||||
if ($this->createAirtimeConfigDirectory()) {
|
||||
$this->moveAirtimeConfig();
|
||||
}
|
||||
}
|
||||
|
||||
function createAirtimeConfigDirectory() {
|
||||
return file_exists("/etc/airtime/") ? true
|
||||
: mkdir("/etc/airtime/", 0755, true);
|
||||
}
|
||||
|
||||
function moveAirtimeConfig() {
|
||||
return copy(AIRTIME_CONF_TEMP_PATH, "/etc/airtime/airtime.conf");
|
||||
}
|
||||
|
||||
}
|
61
airtime_mvc/public/setup/general-setup.php
Normal file
61
airtime_mvc/public/setup/general-setup.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User: sourcefabric
|
||||
* Date: 08/12/14
|
||||
*
|
||||
* Class GeneralSetup
|
||||
*
|
||||
* Wrapper class for validating and setting up general settings during the installation process
|
||||
*/
|
||||
class GeneralSetup extends Setup {
|
||||
|
||||
// airtime.conf section header
|
||||
const SECTION = "[general]";
|
||||
|
||||
// Constant form field names for passing errors back to the front-end
|
||||
const GENERAL_PORT = "generalPort",
|
||||
GENERAL_HOST = "generalHost";
|
||||
|
||||
// Form field values
|
||||
static $user, $host, $port, $root;
|
||||
|
||||
// Array of key->value pairs for airtime.conf
|
||||
static $properties;
|
||||
|
||||
// Message and error fields to return to the front-end
|
||||
static $message = null;
|
||||
static $errors = array();
|
||||
|
||||
function __construct($settings) {
|
||||
self::$host = $settings[self::GENERAL_HOST];
|
||||
self::$port = $settings[self::GENERAL_PORT];
|
||||
|
||||
self::$properties = array(
|
||||
"api_key" => $this->generateRandomString(),
|
||||
"base_url" => self::$host,
|
||||
"base_port" => self::$port,
|
||||
);
|
||||
}
|
||||
|
||||
function writeToTemp() {
|
||||
parent::writeToTemp(self::SECTION, self::$properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
function runSetup() {
|
||||
|
||||
|
||||
if (count(self::$errors) <= 0) {
|
||||
$this->writeToTemp();
|
||||
}
|
||||
|
||||
return array(
|
||||
"message" => self::$message,
|
||||
"errors" => self::$errors
|
||||
);
|
||||
}
|
||||
|
||||
}
|
107
airtime_mvc/public/setup/media-setup.php
Normal file
107
airtime_mvc/public/setup/media-setup.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
define("CONFIG_PATH", dirname(dirname( __DIR__)) . "/application/configs/");
|
||||
|
||||
require_once(dirname(dirname( __DIR__)) . "/library/propel/runtime/lib/Propel.php");
|
||||
require_once(CONFIG_PATH . 'conf.php');
|
||||
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/map/CcMusicDirsTableMap.php");
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/om/BaseCcMusicDirsQuery.php");
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/CcMusicDirsQuery.php");
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/om/BaseCcMusicDirs.php");
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/CcMusicDirs.php");
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/om/BaseCcMusicDirsPeer.php");
|
||||
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/CcMusicDirsPeer.php");
|
||||
|
||||
/**
|
||||
* User: sourcefabric
|
||||
* Date: 08/12/14
|
||||
*
|
||||
* Class MediaSetup
|
||||
*
|
||||
* Wrapper class for validating and setting up media folder during the installation process
|
||||
*/
|
||||
class MediaSetup extends Setup {
|
||||
|
||||
const MEDIA_FOLDER = "mediaFolder";
|
||||
|
||||
static $path;
|
||||
static $message = null;
|
||||
static $errors = array();
|
||||
|
||||
function __construct($settings) {
|
||||
self::$path = $settings[self::MEDIA_FOLDER];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
function runSetup() {
|
||||
// If the path passed in is empty, set it to the default
|
||||
if (strlen(self::$path) == 0) {
|
||||
self::$path = "/srv/airtime/stor/";
|
||||
}
|
||||
|
||||
// Append a trailing / if they didn't
|
||||
if (!(substr(self::$path, -1) == "/")) {
|
||||
self::$path .= "/";
|
||||
}
|
||||
|
||||
if (file_exists(self::$path)) {
|
||||
$this->setupMusicDirectory();
|
||||
} else {
|
||||
self::$message = "Invalid path!";
|
||||
self::$errors[] = self::MEDIA_FOLDER;
|
||||
}
|
||||
|
||||
return array(
|
||||
"message" => self::$message,
|
||||
"errors" => self::$errors
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given directory to cc_music_dirs
|
||||
* TODO Should we check for an existing entry in cc_music_dirs?
|
||||
*/
|
||||
function setupMusicDirectory() {
|
||||
try {
|
||||
$_SERVER['AIRTIME_CONF'] = AIRTIME_CONF_TEMP_PATH;
|
||||
Propel::init(CONFIG_PATH . "airtime-conf-production.php");
|
||||
$con = Propel::getConnection();
|
||||
} catch(Exception $e) {
|
||||
self::$message = "Failed to insert media folder; database isn't configured properly!";
|
||||
self::$errors[] = self::MEDIA_FOLDER;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->runMusicDirsQuery($con);
|
||||
}
|
||||
|
||||
function runMusicDirsQuery($con) {
|
||||
try {
|
||||
if ($this->checkMusicDirectoryExists($con)) {
|
||||
$dir = CcMusicDirsQuery::create()->findPk(1, $con);
|
||||
} else {
|
||||
$dir = new CcMusicDirs();
|
||||
}
|
||||
|
||||
$dir->setDirectory(self::$path)
|
||||
->setType("stor")
|
||||
->save();
|
||||
self::$message = "Successfully set up media folder!";
|
||||
Propel::close();
|
||||
unset($_SERVER['AIRTIME_CONF']);
|
||||
} catch (Exception $e) {
|
||||
self::$message = "Failed to insert " . self::$path . " into cc_music_dirs";
|
||||
self::$errors[] = self::MEDIA_FOLDER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkMusicDirectoryExists($con) {
|
||||
$entry = CcMusicDirsQuery::create()->findPk(1, $con);
|
||||
return isset($entry) && $entry;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
require_once dirname(dirname( __DIR__)) . '/library/php-amqplib/amqp.inc';
|
||||
|
||||
/**
|
||||
* User: sourcefabric
|
||||
* Date: 02/12/14
|
||||
|
@ -9,13 +12,25 @@
|
|||
*/
|
||||
class RabbitMQSetup extends Setup {
|
||||
|
||||
// airtime.conf section header
|
||||
const SECTION = "[rabbitmq]";
|
||||
|
||||
// Constant form field names for passing errors back to the front-end
|
||||
const RMQ_USER = "rmqUser",
|
||||
RMQ_PASS = "rmqPass",
|
||||
RMQ_PORT = "rmqPort",
|
||||
RMQ_HOST = "rmqHost",
|
||||
RMQ_VHOST = "rmqVHost";
|
||||
|
||||
static $user, $pass, $name, $host, $port, $vhost;
|
||||
// Form field values
|
||||
static $user, $pass, $host, $port, $vhost;
|
||||
|
||||
// Array of key->value pairs for airtime.conf
|
||||
static $properties;
|
||||
|
||||
// Message and error fields to return to the front-end
|
||||
static $message = null;
|
||||
static $errors = array();
|
||||
|
||||
function __construct($settings) {
|
||||
self::$user = $settings[self::RMQ_USER];
|
||||
|
@ -23,19 +38,63 @@ class RabbitMQSetup extends Setup {
|
|||
self::$port = $settings[self::RMQ_PORT];
|
||||
self::$host = $settings[self::RMQ_HOST];
|
||||
self::$vhost = $settings[self::RMQ_VHOST];
|
||||
|
||||
self::$properties = array(
|
||||
"host" => self::$host,
|
||||
"port" => self::$port,
|
||||
"user" => self::$user,
|
||||
"password" => self::$pass,
|
||||
"vhost" => self::$vhost,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
function runSetup() {
|
||||
$message = "";
|
||||
$errors = array();
|
||||
try {
|
||||
if ($this->checkRMQConnection()) {
|
||||
self::$message = "Connection successful!";
|
||||
} else {
|
||||
$this->identifyRMQConnectionError();
|
||||
}
|
||||
} catch(Exception $e) {
|
||||
$this->identifyRMQConnectionError();
|
||||
}
|
||||
|
||||
if (count(self::$errors) <= 0) {
|
||||
$this->writeToTemp();
|
||||
}
|
||||
|
||||
return array(
|
||||
"message" => $message,
|
||||
"errors" => $errors
|
||||
"message" => self::$message,
|
||||
"errors" => self::$errors
|
||||
);
|
||||
}
|
||||
|
||||
function writeToTemp() {
|
||||
parent::writeToTemp(self::SECTION, self::$properties);
|
||||
}
|
||||
|
||||
function checkRMQConnection() {
|
||||
$conn = new AMQPConnection(self::$host,
|
||||
self::$port,
|
||||
self::$user,
|
||||
self::$pass,
|
||||
self::$vhost);
|
||||
return isset($conn);
|
||||
}
|
||||
|
||||
function identifyRMQConnectionError() {
|
||||
// It's impossible to identify errors coming out of amqp.inc without a major
|
||||
// rewrite, so for now just tell the user ALL THE THINGS went wrong
|
||||
self::$message = "Couldn't connect to RabbitMQ server! Please check if the server "
|
||||
. "is running and your credentials are correct.";
|
||||
self::$errors[] = self::RMQ_USER;
|
||||
self::$errors[] = self::RMQ_PASS;
|
||||
self::$errors[] = self::RMQ_HOST;
|
||||
self::$errors[] = self::RMQ_PORT;
|
||||
self::$errors[] = self::RMQ_VHOST;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
<?php
|
||||
|
||||
define("BUILD_PATH", dirname(dirname( __DIR__)) . "/build/");
|
||||
define("AIRTIME_CONF_TEMP_PATH", "/tmp/airtime.conf.temp");
|
||||
|
||||
/**
|
||||
* Class Setup
|
||||
*
|
||||
|
@ -11,6 +14,34 @@ abstract class Setup {
|
|||
|
||||
abstract function runSetup();
|
||||
|
||||
protected function writeToTemp($section, $properties) {
|
||||
if (!file_exists(AIRTIME_CONF_TEMP_PATH)) {
|
||||
copy(BUILD_PATH . "airtime.example.conf", AIRTIME_CONF_TEMP_PATH);
|
||||
}
|
||||
|
||||
$file = file(AIRTIME_CONF_TEMP_PATH);
|
||||
$fileOutput = "";
|
||||
|
||||
$inSection = false;
|
||||
|
||||
foreach($file as $line) {
|
||||
if(strpos($line, $section) !== false) {
|
||||
$inSection = true;
|
||||
} else if (strpos($line, "[") !== false) {
|
||||
$inSection = false;
|
||||
}
|
||||
|
||||
if ($inSection) {
|
||||
$key = trim(@substr($line, 0, strpos($line, "=")));
|
||||
$fileOutput .= $key && isset($properties[$key]) ? $key . " = " . $properties[$key] . "\n" : $line;
|
||||
} else {
|
||||
$fileOutput .= $line;
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(AIRTIME_CONF_TEMP_PATH, $fileOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string.
|
||||
*
|
||||
|
@ -18,8 +49,7 @@ abstract class Setup {
|
|||
* @param string $p_chars characters to use in the output string
|
||||
* @return string the generated random string
|
||||
*/
|
||||
protected function generateRandomString($p_len=20, $p_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
|
||||
{
|
||||
protected function generateRandomString($p_len=20, $p_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') {
|
||||
$string = '';
|
||||
for ($i = 0; $i < $p_len; $i++)
|
||||
{
|
||||
|
@ -31,11 +61,16 @@ abstract class Setup {
|
|||
|
||||
}
|
||||
|
||||
// Import Setup subclasses
|
||||
|
||||
require_once('database-setup.php');
|
||||
require_once('rabbitmq-setup.php');
|
||||
require_once('general-setup.php');
|
||||
require_once('media-setup.php');
|
||||
require_once('finish-setup.php');
|
||||
|
||||
// If airtime.conf exists, we shouldn't be here
|
||||
if (!file_exists(dirname(dirname(__DIR__)) . '/build/airtime.conf')) {
|
||||
if (!file_exists("/etc/airtime/airtime.conf")) {
|
||||
if (isset($_GET["obj"]) && $objType = $_GET["obj"]) {
|
||||
$obj = new $objType($_POST);
|
||||
if ($obj instanceof Setup) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue