feat: remove php web installer
This commit is contained in:
parent
eb8e7b3415
commit
0d16960887
|
@ -4,7 +4,6 @@
|
|||
define('ROOT_PATH', dirname(__DIR__, 2));
|
||||
define('LIB_PATH', ROOT_PATH . '/library');
|
||||
define('BUILD_PATH', ROOT_PATH . '/build');
|
||||
define('SETUP_PATH', BUILD_PATH . '/airtime-setup');
|
||||
define('APPLICATION_PATH', ROOT_PATH . '/application');
|
||||
define('CONFIG_PATH', APPLICATION_PATH . '/configs');
|
||||
define('VENDOR_PATH', ROOT_PATH . '/vendor');
|
||||
|
@ -24,10 +23,6 @@ define('LIBRETIME_CONFIG_DIR', getenv('LIBRETIME_CONFIG_DIR') ?: '/etc/libretime
|
|||
define('LIBRETIME_CONF_DIR', LIBRETIME_CONFIG_DIR); // Deprecated
|
||||
define('LIBRETIME_CONFIG_FILEPATH', getenv('LIBRETIME_CONFIG_FILEPATH') ?: LIBRETIME_CONFIG_DIR . '/config.yml');
|
||||
|
||||
// Installer
|
||||
define('INSTALLER_CONFIG_FILEPATH', LIBRETIME_CONFIG_DIR . '/airtime.temp.conf');
|
||||
define('INSTALLER_DEFAULT_STORAGE_PATH', '/srv/airtime/stor');
|
||||
|
||||
// Legacy constants
|
||||
define('PRODUCT_NAME', 'LibreTime');
|
||||
define('PRODUCT_SITE_URL', 'http://libretime.org');
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
$tempConfigPath = INSTALLER_CONFIG_FILEPATH;
|
||||
if (file_exists($tempConfigPath)) {
|
||||
$airtimeConfig = parse_ini_file($tempConfigPath, true);
|
||||
$db = $airtimeConfig['database'];
|
||||
}
|
||||
?>
|
||||
|
||||
<form action="#" role="form" id="dbSettingsForm">
|
||||
<h3 class="form-title">Database Settings</h3>
|
||||
<span id="helpBlock" class="help-block help-message"></span>
|
||||
<p>
|
||||
Enter your Airtime database settings here. Empty or non-existent databases will be created and populated
|
||||
if the given user has administrative permissions in postgres.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="dbUser">Username</label>
|
||||
<input required class="form-control" type="text" name="dbUser" id="dbUser" placeholder="Username" value="<?php echo isset($db) ? $db['user'] : 'airtime'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="dbPass">Password</label>
|
||||
<input required class="form-control" type="password" name="dbPass" id="dbPass" placeholder="Password" value="<?php echo isset($db) ? $db['password'] : 'airtime'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="dbName">Name</label>
|
||||
<input required class="form-control" type="text" name="dbName" id="dbName" placeholder="Name" value="<?php echo isset($db) ? $db['name'] : 'airtime'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="dbHost">Host</label>
|
||||
<input required class="form-control" type="text" name="dbHost" id="dbHost" placeholder="Host" value="<?php echo isset($db) ? $db['host'] : 'localhost'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<input class="form-control" type="hidden" name="dbErr" id="dbErr" aria-describedby="helpBlock" />
|
||||
<div>
|
||||
<p style="text-align:right">
|
||||
This may take up to 30 seconds to complete!
|
||||
</p>
|
||||
<input type="submit" formtarget="dbSettingsForm" class="btn btn-primary btn-next" value="Next ❱" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$("#dbSettingsForm").submit(function(e) {
|
||||
submitForm(e, "DatabaseSetup");
|
||||
});
|
||||
</script>
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
?>
|
||||
|
||||
<form action="#" role="form" id="finishSettingsForm">
|
||||
<h3 class="form-title">Manual Step: Start Libretime Services</h3>
|
||||
<span id="helpBlock" class="help-block help-message"></span>
|
||||
<p>
|
||||
Looks like you're almost done! As a final step, please run the following commands from the terminal:
|
||||
</p>
|
||||
<pre style="text-align: left">sudo systemctl start libretime-analyzer
|
||||
sudo systemctl start libretime-api
|
||||
sudo systemctl start libretime-celery
|
||||
sudo systemctl start libretime-liquidsoap
|
||||
sudo systemctl start libretime-playout</pre>
|
||||
<p>
|
||||
Click "Done!" to bring up the Libretime configuration checklist; if your configuration is all green,
|
||||
you're ready to get started with your personal Libretime station!
|
||||
</p>
|
||||
<p>
|
||||
If you need to re-run the web installer, just remove <code><?php echo LIBRETIME_CONFIG_FILEPATH; ?></code>.
|
||||
</p>
|
||||
<div>
|
||||
<input type="submit" formtarget="finishSettingsForm" class="btn btn-primary btn-next" value="Done!" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$("#finishSettingsForm").submit(function(e) {
|
||||
e.preventDefault();
|
||||
window.location.assign("/?config");
|
||||
});
|
||||
</script>
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
?>
|
||||
|
||||
<form action="#" role="form" id="generalSettingsForm">
|
||||
<h3 class="form-title">General Settings</h3>
|
||||
<span id="helpBlock" class="help-block help-message"></span>
|
||||
<div id="generalFormBody">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="publicUrl">Public URL</label>
|
||||
<input required class="form-control" type="text" name="publicUrl" id="publicUrl" placeholder="https://example.com/" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<input class="form-control" type="hidden" name="generalErr" id="generalErr" aria-describedby="helpBlock" />
|
||||
<hr />
|
||||
<p>
|
||||
The CORS URL can be setup during install if you are accessing your LibreTime instance behind a Proxy.
|
||||
This is common with docker setups. If you have a reverse proxy setup enter the URL below, otherwise you
|
||||
can safely ignore this. Please enter one URL per line. Include the entire URL such as http://example.com
|
||||
If you are reinstalling LibreTime on an existing setup you can ignore this as well,
|
||||
the settings in your existing database will be retained unless you enter new values below.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="corsUrl">CORS URLs</label>
|
||||
<textarea name="corsUrl" class="form-control" id="corsUrl" rows="4" cols="50"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" formtarget="generalSettingsForm" class="btn btn-primary btn-next" value="Next ❱" />
|
||||
<input type="button" class="btn btn-primary btn-back" value="❰ Back" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$("#publicUrl").val(function() {
|
||||
return window.location.href;
|
||||
});
|
||||
$("#corsUrl").text(function() {
|
||||
return window.location.origin;
|
||||
});
|
||||
$("#generalSettingsForm").submit(function(e) {
|
||||
submitForm(e, "GeneralSetup");
|
||||
});
|
||||
</script>
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
?>
|
||||
|
||||
<form action="#" role="form" id="mediaSettingsForm">
|
||||
<h3 class="form-title">Media Settings</h3>
|
||||
<span id="helpBlock" class="help-block help-message"></span>
|
||||
<p>
|
||||
Here you can set the default media storage directory for Airtime. If left blank, we'll create a new
|
||||
directory located at <code><?php echo INSTALLER_DEFAULT_STORAGE_PATH; ?></code> for you.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="mediaFolder">Media folder</label>
|
||||
<input class="form-control" type="text" name="mediaFolder" id="mediaFolder" placeholder="/path/to/my/music/directory/" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
<span id="mediaHelpBlock" class="help-block">
|
||||
Note that you need to enter the <strong>fully qualified</strong> path name!
|
||||
</span>
|
||||
</div>
|
||||
<input class="form-control" type="hidden" name="mediaErr" id="mediaErr" aria-describedby="helpBlock"/>
|
||||
<div>
|
||||
<input type="submit" formtarget="mediaSettingsForm" class="btn btn-primary btn-next" value="Next ❱"/>
|
||||
<input type="button" class="btn btn-primary btn-back" value="❰ Back"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$("#mediaSettingsForm").submit(function(e) {
|
||||
submitForm(e, "MediaSetup");
|
||||
});
|
||||
</script>
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
$tempConfigPath = INSTALLER_CONFIG_FILEPATH;
|
||||
if (file_exists($tempConfigPath)) {
|
||||
$airtimeConfig = parse_ini_file($tempConfigPath, true);
|
||||
$rmq = $airtimeConfig['rabbitmq'];
|
||||
}
|
||||
?>
|
||||
|
||||
<form action="#" role="form" id="rmqSettingsForm">
|
||||
<h3 class="form-title">RabbitMQ Settings</h3>
|
||||
<span id="helpBlock" class="help-block help-message"></span>
|
||||
<p>
|
||||
RabbitMQ is an AMQP-based messaging system used by Libretime. You should only edit these settings
|
||||
if you have changed the defaults since running the installer, or if you've opted to install RabbitMQ manually.
|
||||
</p>
|
||||
<p>
|
||||
In either case, we recommend that you change at least the default password provided -
|
||||
you can do this by running the following line from the command line:<br/>
|
||||
<code>sudo rabbitmqctl change_password <username> <newpassword></code><br/>
|
||||
<strong>Notice:</strong> using special characters such as ! in your rabbitmq password will cause LibreTime to fail
|
||||
to load properly after setup. Please use alphanumerical characters only.
|
||||
</p>
|
||||
<div id="rmqSlideToggle">
|
||||
<span><strong>Advanced </strong></span><span id="advCaret" class="caret"></span><hr/>
|
||||
</div>
|
||||
<div id="rmqFormBody">
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="rmqUser">Username</label>
|
||||
<input required class="form-control" type="text" name="rmqUser" id="rmqUser" placeholder="Username"
|
||||
value="<?php echo isset($rmq) ? $rmq['user'] : 'airtime'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="rmqPass">Password</label>
|
||||
<input class="form-control" type="password" name="rmqPass" id="rmqPass" placeholder="Password"
|
||||
value="<?php echo isset($rmq) ? $rmq['password'] : 'airtime'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
<span id="rmqHelpBlock" class="help-block">
|
||||
You probably want to change this!
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="rmqHost">Host</label>
|
||||
<input required class="form-control" type="text" name="rmqHost" id="rmqHost" placeholder="Host"
|
||||
value="<?php echo isset($rmq) ? $rmq['host'] : '127.0.0.1'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="rmqPort">Port</label>
|
||||
<input required class="form-control" type="text" name="rmqPort" id="rmqPort" placeholder="Port"
|
||||
value="<?php echo isset($rmq) ? $rmq['port'] : '5672'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="rmqVHost">Virtual Host</label>
|
||||
<input required class="form-control" type="text" name="rmqVHost" id="rmqVHost" placeholder="VHost"
|
||||
value="<?php echo isset($rmq) ? $rmq['vhost'] : '/airtime'; ?>" />
|
||||
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
|
||||
</div>
|
||||
<input class="form-control" type="hidden" name="rmqErr" id="rmqErr" aria-describedby="helpBlock"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" formtarget="rmqSettingsForm" class="btn btn-primary btn-next" value="Next ❱"/>
|
||||
<input type="button" class="btn btn-primary btn-back" value="❰ Back"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$("#rmqSlideToggle").click(function() {
|
||||
$("#rmqFormBody").slideToggle(500);
|
||||
$("#advCaret").toggleClass("caret-up");
|
||||
});
|
||||
|
||||
$("#rmqSettingsForm").submit(function(e) {
|
||||
submitForm(e, "RabbitMQSetup");
|
||||
});
|
||||
</script>
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/application/preload.php';
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="js/libs/jquery-1.8.3.min.js"></script>
|
||||
<script type="text/javascript" src="js/libs/jquery-ui-1.8.24.min.js"></script>
|
||||
<script type="text/javascript" src="js/setup/setup-config.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap-3.3.1.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="css/setup/setup-config.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h3 class="logo">
|
||||
<img src="css/images/libretime_logo_jp.png" id="LibreTimeLogo" /><br/>
|
||||
<strong>Setup</strong>
|
||||
</h3>
|
||||
<strong>Step <span id="stepCount">1</span> of 5</strong>
|
||||
</div>
|
||||
|
||||
<div class="viewport">
|
||||
<div class="form-slider">
|
||||
<div id="databaseSettings" class="form-wrapper">
|
||||
<?php
|
||||
require_once SETUP_PATH . '/forms/database-settings.php';
|
||||
?>
|
||||
</div>
|
||||
<div id="rabbitmqSettings" class="form-wrapper">
|
||||
<?php
|
||||
require_once SETUP_PATH . '/forms/rabbitmq-settings.php';
|
||||
?>
|
||||
</div>
|
||||
<div id="generalSettings" class="form-wrapper">
|
||||
<?php
|
||||
require_once SETUP_PATH . '/forms/general-settings.php';
|
||||
?>
|
||||
</div>
|
||||
<div id="mediaSettings" class="form-wrapper">
|
||||
<?php
|
||||
require_once SETUP_PATH . '/forms/media-settings.php';
|
||||
?>
|
||||
</div>
|
||||
<div id="finishSettings" class="form-wrapper">
|
||||
<?php
|
||||
require_once SETUP_PATH . '/forms/finish-settings.php';
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(".btn-skip").click(nextSlide);
|
||||
$(".btn-back").click(prevSlide);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
require_once dirname(__DIR__) . '/application/preload.php';
|
||||
|
||||
// Early exit if a configuration file does not exists!
|
||||
if (!file_exists(LIBRETIME_CONFIG_FILEPATH)) {
|
||||
exit("could not find a configuration file at '{$LIBRETIME_CONFIG_FILEPATH}', please make sure it is properly set!");
|
||||
}
|
||||
|
||||
$configRun = false;
|
||||
$extensions = get_loaded_extensions();
|
||||
$airtimeSetup = false;
|
||||
|
@ -40,7 +45,7 @@ if (!class_exists('Propel')) {
|
|||
exit('Error: Propel not found. Did you install Airtime\'s third-party dependencies with composer? (Check the README.)');
|
||||
}
|
||||
|
||||
require_once SETUP_PATH . '/load.php';
|
||||
require_once APPLICATION_PATH . '/check.php';
|
||||
|
||||
// This allows us to pass ?config as a parameter to any page
|
||||
// and get to the config checklist.
|
||||
|
@ -48,13 +53,4 @@ if (array_key_exists('config', $_GET)) {
|
|||
showConfigCheckPage();
|
||||
}
|
||||
|
||||
// If a configuration file exists, forward to our boot script
|
||||
if (file_exists(LIBRETIME_CONFIG_FILEPATH)) {
|
||||
require_once APPLICATION_PATH . '/airtime-boot.php';
|
||||
}
|
||||
// Otherwise, we'll need to run our configuration setup
|
||||
else {
|
||||
$airtimeSetup = true;
|
||||
|
||||
require_once SETUP_PATH . '/setup-config.php';
|
||||
}
|
||||
require_once APPLICATION_PATH . '/airtime-boot.php';
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* The purpose of this file is get PHP to clear its cache regarding the
|
||||
* filesystem layout. See this ticket http://dev.sourcefabric.org/browse/CC-3320 */
|
||||
|
||||
clearstatcache(true);
|
|
@ -1,293 +0,0 @@
|
|||
<?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
|
||||
{
|
||||
// config file section header
|
||||
protected static $_section = '[database]';
|
||||
|
||||
// Constant form field names for passing errors back to the front-end
|
||||
public const DB_USER = 'dbUser';
|
||||
public const DB_PASS = 'dbPass';
|
||||
public const DB_NAME = 'dbName';
|
||||
public const DB_HOST = 'dbHost';
|
||||
|
||||
// Array of key->value pairs for the config file
|
||||
protected static $_properties;
|
||||
|
||||
/**
|
||||
* @var PDO
|
||||
*/
|
||||
public static $dbh;
|
||||
|
||||
public function __construct($settings)
|
||||
{
|
||||
static::$_properties = [
|
||||
'host' => $settings[self::DB_HOST],
|
||||
'name' => $settings[self::DB_NAME],
|
||||
'user' => $settings[self::DB_USER],
|
||||
'password' => $settings[self::DB_PASS],
|
||||
];
|
||||
}
|
||||
|
||||
private function setNewDatabaseConnection($dbName)
|
||||
{
|
||||
self::$dbh = new PDO('pgsql:host=' . self::$_properties['host'] . ';dbname=' . $dbName . ';port=5432'
|
||||
. ';user=' . self::$_properties['user'] . ';password=' . self::$_properties['password']);
|
||||
$err = self::$dbh->errorInfo();
|
||||
if ($err[1] != null) {
|
||||
throw new PDOException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs various database checks against the given settings. If a database with the given name already exists,
|
||||
* we attempt to install the Airtime schema. If not, we first check if the user can create databases, then try
|
||||
* to create the database. If we encounter errors, the offending fields are returned in an array to the browser.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
public function runSetup()
|
||||
{
|
||||
$this->writeToTemp();
|
||||
|
||||
try {
|
||||
$this->setNewDatabaseConnection('postgres');
|
||||
if ($this->checkDatabaseExists()) {
|
||||
$this->installDatabaseTables();
|
||||
} else {
|
||||
$this->checkUserCanCreateDb();
|
||||
$this->createDatabase();
|
||||
$this->installDatabaseTables();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
throw new AirtimeDatabaseException(
|
||||
"Couldn't establish a connection to the database! " .
|
||||
'Please check your credentials and try again. '
|
||||
. 'PDO Exception: ' . $e->getMessage(),
|
||||
[self::DB_NAME, self::DB_USER, self::DB_PASS]
|
||||
);
|
||||
}
|
||||
self::$dbh = null;
|
||||
|
||||
return [
|
||||
'message' => 'Airtime database was created successfully!',
|
||||
'errors' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function installDatabaseTables()
|
||||
{
|
||||
$this->checkDatabaseEncoding();
|
||||
$this->setNewDatabaseConnection(self::$_properties['name']);
|
||||
$this->checkSchemaExists();
|
||||
$this->createDatabaseTables();
|
||||
$this->updateIcecastPassword();
|
||||
$this->updateDjangoTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database settings and credentials given are valid.
|
||||
*
|
||||
* @return bool true if the database given exists and the user is valid and can access it
|
||||
*/
|
||||
private function checkDatabaseExists()
|
||||
{
|
||||
$statement = self::$dbh->prepare('SELECT datname FROM pg_database WHERE datname = :dbname');
|
||||
$statement->execute([':dbname' => self::$_properties['name']]);
|
||||
$result = $statement->fetch();
|
||||
|
||||
return isset($result[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database schema has already been set up.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*/
|
||||
private function checkSchemaExists()
|
||||
{
|
||||
$statement = self::$dbh->prepare("SELECT EXISTS (SELECT relname FROM pg_class WHERE relname='cc_files')");
|
||||
$statement->execute();
|
||||
$result = $statement->fetch();
|
||||
if (isset($result[0]) && $result[0] == 't') {
|
||||
throw new AirtimeDatabaseException('Airtime is already installed in this database!', []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given user has access on the given host to create a new database.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*/
|
||||
private function checkUserCanCreateDb()
|
||||
{
|
||||
$statement = self::$dbh->prepare("SELECT 1 FROM pg_roles WHERE rolname=:dbuser AND rolcreatedb='t'");
|
||||
$statement->execute([':dbuser' => self::$_properties['user']]);
|
||||
$result = $statement->fetch();
|
||||
if (!isset($result[0])) {
|
||||
throw new AirtimeDatabaseException(
|
||||
'No database ' . self::$_properties['name'] . " exists; user '"
|
||||
. self::$_properties['user'] . "' does not have permission to "
|
||||
. 'create databases on ' . self::$_properties['host'],
|
||||
[self::DB_NAME, self::DB_USER, self::DB_PASS]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Airtime database using the given credentials.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*/
|
||||
private function createDatabase()
|
||||
{
|
||||
$statement = self::$dbh->prepare('CREATE DATABASE ' . pg_escape_string(self::$_properties['name'])
|
||||
. " WITH ENCODING 'UNICODE' TEMPLATE template0"
|
||||
. ' OWNER ' . pg_escape_string(self::$_properties['user']));
|
||||
if (!$statement->execute()) {
|
||||
throw new AirtimeDatabaseException(
|
||||
'There was an error creating the database!',
|
||||
[self::DB_NAME]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Airtime database schema using the given credentials.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*/
|
||||
private function createDatabaseTables()
|
||||
{
|
||||
$sqlDir = dirname(ROOT_PATH) . '/api/libretime_api/legacy/migrations/sql/';
|
||||
$files = ['schema.sql', 'data.sql'];
|
||||
foreach ($files as $file) {
|
||||
try {
|
||||
$sql = file_get_contents($sqlDir . $file);
|
||||
self::$dbh->exec($sql);
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage();
|
||||
|
||||
throw new AirtimeDatabaseException(
|
||||
'There was an error setting up the Airtime schema!: ' . $e->getMessage(),
|
||||
[self::DB_NAME]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the newly-created database's encoding was properly set to UTF8.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*/
|
||||
private function checkDatabaseEncoding()
|
||||
{
|
||||
$statement = self::$dbh->prepare('SELECT pg_encoding_to_char(encoding) '
|
||||
. 'FROM pg_database WHERE datname = :dbname');
|
||||
$statement->execute([':dbname' => self::$_properties['name']]);
|
||||
$encoding = $statement->fetch();
|
||||
if (!($encoding && $encoding[0] == 'UTF8')) {
|
||||
throw new AirtimeDatabaseException(
|
||||
'The database was installed with an incorrect encoding type!',
|
||||
[self::DB_NAME]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the icecast password in the database based upon the temp file created during install.
|
||||
*
|
||||
* @throws AirtimeDatabaseException
|
||||
*/
|
||||
private function updateIcecastPassword()
|
||||
{
|
||||
if (!file_exists(LIBRETIME_CONF_DIR . '/icecast_pass')) {
|
||||
throw new AirtimeDatabaseException('The Icecast Password file was not accessible', []);
|
||||
}
|
||||
$icecast_pass_txt = file(LIBRETIME_CONF_DIR . '/icecast_pass');
|
||||
$icecast_pass = $icecast_pass_txt[0];
|
||||
$icecast_pass = str_replace(PHP_EOL, '', $icecast_pass);
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_admin_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's2_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's2_admin_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's3_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's3_admin_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
$statement = self::$dbh->prepare("UPDATE cc_stream_setting SET value = :icecastpass WHERE keyname = 's1_admin_pass'");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
$statement = self::$dbh->prepare("INSERT INTO cc_pref (keystr, valstr) VALUES ('default_icecast_password', :icecastpass )");
|
||||
$statement->bindValue(':icecastpass', $icecast_pass, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
$statement->execute();
|
||||
} catch (PDOException $ex) {
|
||||
echo 'Error!: ' . $ex->getMessage() . '<br />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Django related tables for the API.
|
||||
*/
|
||||
private function updateDjangoTables()
|
||||
{
|
||||
shell_exec('DJANGO_SETTINGS_MODULE=libretime_api.settings.testing LIBRETIME_CONFIG_FILEPATH=' . INSTALLER_CONFIG_FILEPATH . ' libretime-api migrate');
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
<?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
|
||||
{
|
||||
// config file section header
|
||||
protected static $_section = '[general]';
|
||||
|
||||
// Array of key->value pairs for the config file
|
||||
protected static $_properties;
|
||||
|
||||
// Constant form field names for passing errors back to the front-end
|
||||
public const PUBLIC_URL = 'publicUrl';
|
||||
public const CORS_URL = 'corsUrl';
|
||||
|
||||
public static $cors_url;
|
||||
|
||||
// Message and error fields to return to the front-end
|
||||
public static $message;
|
||||
public static $errors = [];
|
||||
|
||||
public function __construct($settings)
|
||||
{
|
||||
self::$_properties = [
|
||||
'api_key' => $this->generateRandomString(),
|
||||
'public_url' => $settings[self::PUBLIC_URL],
|
||||
'cors_url' => $settings[self::CORS_URL],
|
||||
];
|
||||
self::$cors_url = $settings[self::CORS_URL];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
public function runSetup()
|
||||
{
|
||||
// TODO Do we need to validate these settings?
|
||||
|
||||
if (count(self::$errors) <= 0) {
|
||||
$this->writeToTemp();
|
||||
}
|
||||
if (strlen(self::$cors_url) == 0) {
|
||||
} else {
|
||||
$this->setupCorsUrl();
|
||||
}
|
||||
|
||||
return [
|
||||
'message' => self::$message,
|
||||
'errors' => self::$errors,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user entered a CORS Url then add it to the system preferences
|
||||
* TODO Make sure we check for existing CORS URLs and display them on initial form.
|
||||
*/
|
||||
public function setupCorsUrl()
|
||||
{
|
||||
try {
|
||||
$_SERVER['LIBRETIME_CONFIG_FILEPATH'] = INSTALLER_CONFIG_FILEPATH;
|
||||
Propel::init(PROPEL_CONFIG_FILEPATH);
|
||||
$con = Propel::getConnection();
|
||||
} catch (Exception $e) {
|
||||
self::$message = "Failed to insert Cors URL; database isn't configured properly!";
|
||||
self::$errors[] = self::CORS_URL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->runCorsUrlQuery($con);
|
||||
}
|
||||
|
||||
public function runCorsUrlQuery($con)
|
||||
{
|
||||
try {
|
||||
Application_Model_Preference::SetAllowedCorsUrls(self::$cors_url);
|
||||
Propel::close();
|
||||
} catch (Exception $e) {
|
||||
self::$message = 'Failed to insert ' . self::$cors_url . ' into cc_pref' . $e;
|
||||
self::$errors[] = self::CORS_URL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Author: sourcefabric
|
||||
* Date: 08/12/14.
|
||||
*
|
||||
* Class MediaSetup
|
||||
*
|
||||
* Wrapper class for validating and setting up media folder during the installation process
|
||||
*/
|
||||
class MediaSetup extends Setup
|
||||
{
|
||||
public const MEDIA_FOLDER = 'mediaFolder';
|
||||
|
||||
public static $path;
|
||||
public static $message;
|
||||
public static $errors = [];
|
||||
|
||||
public function __construct($settings)
|
||||
{
|
||||
self::$path = $settings[self::MEDIA_FOLDER];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
public function runSetup()
|
||||
{
|
||||
// If the path passed in is empty, set it to the default
|
||||
if (strlen(self::$path) == 0) {
|
||||
self::$path = INSTALLER_DEFAULT_STORAGE_PATH;
|
||||
if (!file_exists(INSTALLER_DEFAULT_STORAGE_PATH)) {
|
||||
mkdir(INSTALLER_DEFAULT_STORAGE_PATH, 0755, true);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Finalize and move installer config file to libretime config file
|
||||
if (file_exists(LIBRETIME_CONFIG_DIR)) {
|
||||
if (!$this->moveAirtimeConfig()) {
|
||||
self::$message = 'Error moving or deleting the installer config!';
|
||||
self::$errors[] = 'ERR';
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're upgrading from an old Airtime instance (pre-2.5.2) we rename their old
|
||||
* config.yml to config.yml.tmp during the setup process. Now that we're done,
|
||||
* we can rename it to config.yml.bak to avoid confusion.
|
||||
*/
|
||||
$fileName = LIBRETIME_CONFIG_FILEPATH;
|
||||
$tmpFile = $fileName . '.tmp';
|
||||
$bakFile = $fileName . '.bak';
|
||||
if (file_exists($tmpFile)) {
|
||||
rename($tmpFile, $bakFile);
|
||||
}
|
||||
} else {
|
||||
self::$message = "Failed to move config.yml; /etc/libretime doesn't exist!";
|
||||
self::$errors[] = 'ERR';
|
||||
}
|
||||
|
||||
return [
|
||||
'message' => self::$message,
|
||||
'errors' => self::$errors,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves /tmp/airtime.temp.conf to /etc/libretime/config.yml and then removes it to complete setup.
|
||||
*
|
||||
* @return bool false if either of the copy or removal operations fail
|
||||
*/
|
||||
public function moveAirtimeConfig()
|
||||
{
|
||||
return copy(INSTALLER_CONFIG_FILEPATH, LIBRETIME_CONFIG_FILEPATH)
|
||||
&& unlink(INSTALLER_CONFIG_FILEPATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given directory to cc_music_dirs
|
||||
* TODO Should we check for an existing entry in cc_music_dirs?
|
||||
*/
|
||||
public function setupMusicDirectory()
|
||||
{
|
||||
try {
|
||||
$_SERVER['LIBRETIME_CONFIG_FILEPATH'] = INSTALLER_CONFIG_FILEPATH;
|
||||
Propel::init(PROPEL_CONFIG_FILEPATH);
|
||||
$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);
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMusicDirectoryExists($con)
|
||||
{
|
||||
$entry = CcMusicDirsQuery::create()->findPk(1, $con);
|
||||
|
||||
return isset($entry) && $entry;
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User: sourcefabric
|
||||
* Date: 02/12/14.
|
||||
*
|
||||
* Class RabbitMQSetup
|
||||
*
|
||||
* Wrapper class for validating and setting up RabbitMQ during the installation process
|
||||
*/
|
||||
class RabbitMQSetup extends Setup
|
||||
{
|
||||
// config.yml section header
|
||||
protected static $_section = '[rabbitmq]';
|
||||
|
||||
// Array of key->value pairs for config.yml
|
||||
protected static $_properties;
|
||||
|
||||
// Constant form field names for passing errors back to the front-end
|
||||
public const RMQ_USER = 'rmqUser';
|
||||
public const RMQ_PASS = 'rmqPass';
|
||||
public const RMQ_PORT = 'rmqPort';
|
||||
public const RMQ_HOST = 'rmqHost';
|
||||
public const RMQ_VHOST = 'rmqVHost';
|
||||
|
||||
// Message and error fields to return to the front-end
|
||||
public static $message;
|
||||
public static $errors = [];
|
||||
|
||||
public function __construct($settings)
|
||||
{
|
||||
static::$_properties = [
|
||||
'host' => $settings[self::RMQ_HOST],
|
||||
'port' => $settings[self::RMQ_PORT],
|
||||
'user' => $settings[self::RMQ_USER],
|
||||
'password' => $settings[self::RMQ_PASS],
|
||||
'vhost' => $settings[self::RMQ_VHOST],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array associative array containing a display message and fields with errors
|
||||
*/
|
||||
public function runSetup()
|
||||
{
|
||||
try {
|
||||
if ($this->checkRMQConnection()) {
|
||||
self::$message = 'Connection successful!';
|
||||
} else {
|
||||
$this->identifyRMQConnectionError();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->identifyRMQConnectionError();
|
||||
}
|
||||
|
||||
return [
|
||||
'message' => self::$message,
|
||||
'errors' => self::$errors,
|
||||
];
|
||||
}
|
||||
|
||||
public function checkRMQConnection()
|
||||
{
|
||||
$conn = new \PhpAmqpLib\Connection\AMQPStreamConnection(
|
||||
self::$_properties['host'],
|
||||
self::$_properties['port'],
|
||||
self::$_properties['user'],
|
||||
self::$_properties['password'],
|
||||
self::$_properties['vhost']
|
||||
);
|
||||
$this->writeToTemp();
|
||||
|
||||
return isset($conn);
|
||||
}
|
||||
|
||||
public 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,143 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/application/preload.php';
|
||||
|
||||
/**
|
||||
* Class Setup.
|
||||
*
|
||||
* @author sourcefabric
|
||||
*
|
||||
* Abstract superclass for the setup and installation process
|
||||
*/
|
||||
abstract class Setup
|
||||
{
|
||||
protected static $_section;
|
||||
|
||||
/**
|
||||
* Array of key->value pairs for config.yml.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $_properties;
|
||||
|
||||
abstract public function __construct($settings);
|
||||
|
||||
abstract public function runSetup();
|
||||
|
||||
/**
|
||||
* Write new property values to a given section in airtime.temp.conf.
|
||||
*/
|
||||
protected function writeToTemp()
|
||||
{
|
||||
if (!file_exists(INSTALLER_CONFIG_FILEPATH)) {
|
||||
copy(SAMPLE_CONFIG_FILEPATH, INSTALLER_CONFIG_FILEPATH);
|
||||
}
|
||||
// Logging::info(CONFIG_TEMP_FILEPATH);
|
||||
$this->_write(INSTALLER_CONFIG_FILEPATH);
|
||||
}
|
||||
|
||||
protected function _write($filePath)
|
||||
{
|
||||
$file = file($filePath);
|
||||
$fileOutput = '';
|
||||
|
||||
$inSection = false;
|
||||
|
||||
foreach ($file as $line) {
|
||||
if (strpos($line, static::$_section) !== false) {
|
||||
$inSection = true;
|
||||
} elseif (strpos($line, '[') !== false) {
|
||||
$inSection = false;
|
||||
}
|
||||
|
||||
if (substr(trim($line), 0, 1) == '#') {
|
||||
/* Workaround to strip comments from config.yml.
|
||||
* We need to do this because python's ConfigObj and PHP
|
||||
* have different (and equally strict) rules about comment
|
||||
* characters in configuration files.
|
||||
*/
|
||||
} elseif ($inSection) {
|
||||
$key = trim(@substr($line, 0, strpos($line, '=')));
|
||||
$fileOutput .= $key && isset(static::$_properties[$key]) ?
|
||||
$key . ' = ' . static::$_properties[$key] . "\n" : $line;
|
||||
} else {
|
||||
$fileOutput .= $line;
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($filePath, $fileOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string.
|
||||
*
|
||||
* @param int $p_len
|
||||
* length of the output string
|
||||
* @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')
|
||||
{
|
||||
$string = '';
|
||||
for ($i = 0; $i < $p_len; ++$i) {
|
||||
$pos = random_int(0, strlen($p_chars) - 1);
|
||||
$string .= $p_chars[$pos];
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class AirtimeDatabaseException.
|
||||
*
|
||||
* @author sourcefabric
|
||||
*
|
||||
* Exception class for database setup errors
|
||||
*/
|
||||
class AirtimeDatabaseException extends Exception
|
||||
{
|
||||
protected $message = 'Unknown Airtime database exception';
|
||||
protected $errors = [];
|
||||
|
||||
public function __construct($message = null, $errors = [], $code = 0, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->errors = $errors;
|
||||
}
|
||||
|
||||
public function getErrorFields()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
|
||||
// Import Setup subclasses
|
||||
require_once 'database-setup.php';
|
||||
|
||||
require_once 'rabbitmq-setup.php';
|
||||
|
||||
require_once 'general-setup.php';
|
||||
|
||||
require_once 'media-setup.php';
|
||||
|
||||
// If config.yml exists, we shouldn't be here
|
||||
if (!file_exists('/etc/libretime/config.yml')) {
|
||||
if (isset($_GET['obj']) && $objType = $_GET['obj']) {
|
||||
$obj = new $objType($_POST);
|
||||
if ($obj instanceof Setup) {
|
||||
try {
|
||||
$response = $obj->runSetup();
|
||||
} catch (AirtimeDatabaseException $e) {
|
||||
$response = [
|
||||
'message' => $e->getMessage(),
|
||||
'errors' => $e->getErrorFields(),
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue