Improved install wizard

This commit is contained in:
Sergio Brighenti 2019-05-23 21:24:04 +02:00
parent 55cd988c79
commit f47bc3241a
10 changed files with 267 additions and 712 deletions

1
.gitignore vendored
View file

@ -130,6 +130,7 @@ fabric.properties
# Repo file
storage/
static/
install/installer.js
!resources/cache/.gitkeep
resources/cache/*
resources/database/*.db

View file

@ -26,6 +26,9 @@ module.exports = function (grunt) {
'static/app/app.js': [
'src/js/app.js'
],
'install/installer.js': [
'src/js/installer.js'
],
}
}
},
@ -41,6 +44,7 @@ module.exports = function (grunt) {
scripts: {
files: [
'src/js/app.js',
'src/js/installer.js',
],
tasks: ['uglify']

View file

@ -26,8 +26,9 @@ class DB
/** @var string */
protected $currentDriver;
public function __construct(string $dsn, $username, $password)
public function __construct(string $dsn, string $username = null, string $password = null)
{
self::setDsn($dsn, $username, $password);
$this->pdo = new PDO($dsn, $username, $password);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

36
composer.lock generated
View file

@ -1,23 +1,23 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "d2c8e7bde7729d78cef484491535c639",
"packages": [
{
"name": "aws/aws-sdk-php",
"version": "3.93.12",
"version": "3.94.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "60f5c4bd261e19844d4263c3a8f370e88e038282"
"reference": "96fd506d4000cc8f3909154e2dd1e4180835941e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/60f5c4bd261e19844d4263c3a8f370e88e038282",
"reference": "60f5c4bd261e19844d4263c3a8f370e88e038282",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/96fd506d4000cc8f3909154e2dd1e4180835941e",
"reference": "96fd506d4000cc8f3909154e2dd1e4180835941e",
"shasum": ""
},
"require": {
@ -87,7 +87,7 @@
"s3",
"sdk"
],
"time": "2019-05-17T18:26:46+00:00"
"time": "2019-05-22T18:11:35+00:00"
},
{
"name": "container-interop/container-interop",
@ -1340,16 +1340,16 @@
},
{
"name": "spatie/dropbox-api",
"version": "1.8.0",
"version": "1.9.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/dropbox-api.git",
"reference": "dc930cde4fdb802d20d0aeef5a98c3805fdcf9c2"
"reference": "72a7c833ada88b8eb0af7cc1c561e02b0c9a5987"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/dropbox-api/zipball/dc930cde4fdb802d20d0aeef5a98c3805fdcf9c2",
"reference": "dc930cde4fdb802d20d0aeef5a98c3805fdcf9c2",
"url": "https://api.github.com/repos/spatie/dropbox-api/zipball/72a7c833ada88b8eb0af7cc1c561e02b0c9a5987",
"reference": "72a7c833ada88b8eb0af7cc1c561e02b0c9a5987",
"shasum": ""
},
"require": {
@ -1392,7 +1392,7 @@
"spatie",
"v2"
],
"time": "2019-04-13T10:52:11+00:00"
"time": "2019-05-21T12:13:37+00:00"
},
{
"name": "spatie/flysystem-dropbox",
@ -1538,7 +1538,7 @@
},
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
"email": "backendtea@gmail.com"
}
],
"description": "Symfony polyfill for ctype functions",
@ -1612,16 +1612,16 @@
},
{
"name": "twig/twig",
"version": "v2.9.0",
"version": "v2.10.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "82a1c055c8ed4c4705023bfd0405f3c74db6e3ae"
"reference": "5240e21982885b76629552d83b4ebb6d41ccde6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/82a1c055c8ed4c4705023bfd0405f3c74db6e3ae",
"reference": "82a1c055c8ed4c4705023bfd0405f3c74db6e3ae",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/5240e21982885b76629552d83b4ebb6d41ccde6b",
"reference": "5240e21982885b76629552d83b4ebb6d41ccde6b",
"shasum": ""
},
"require": {
@ -1637,7 +1637,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.9-dev"
"dev-master": "2.10-dev"
}
},
"autoload": {
@ -1675,7 +1675,7 @@
"keywords": [
"templating"
],
"time": "2019-04-28T06:57:38+00:00"
"time": "2019-05-14T12:03:52+00:00"
}
],
"packages-dev": [

View file

@ -1,11 +1,14 @@
<?php
return [
'base_url' => 'http://localhost',
'storage_dir' => 'storage',
'db' => [
'connection' => 'sqlite',
'dsn' => 'resources/database/xbackbone.db',
'username' => null,
'password' => null,
]
],
'storage' => [
'driver' => 'local',
'path' => './storage',
],
];

View file

@ -25,6 +25,7 @@ define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer
$config = [
'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
'displayErrorDetails' => true,
'db' => [
'connection' => 'sqlite',
'dsn' => 'resources/database/xbackbone.db',
@ -226,8 +227,8 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
$config['storage']['password'] = $request->getParam('storage_password');
$config['storage']['port'] = $request->getParam('storage_port');
$config['storage']['path'] = $request->getParam('storage_path');
$config['storage']['passive'] = $request->getParam('storage_passive');
$config['storage']['ssl'] = $request->getParam('storage_ssl');
$config['storage']['passive'] = $request->getParam('storage_passive') === '1';
$config['storage']['ssl'] = $request->getParam('storage_ssl') === '1';
break;
case 'google-cloud':
$config['storage']['project_id'] = $request->getParam('storage_project_id');
@ -242,20 +243,20 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
// check if the storage is valid
try {
$success = $this->storage->write('test.install.txt', '');
$this->storage->readAndDelete('test.install.txt');
$success = $this->storage->write('storage_test.xbackbone.txt', 'XBACKBONE_TEST_FILE');
if (!$success) {
throw new Exception('The storage is not writable.');
}
$this->storage->readAndDelete('test.install.txt');
} catch (Exception $e) {
$this->session->alert("Storage setup error: {$e->getMessage()} [{$e->getTraceAsString()}]", 'danger');
return redirect($response, './');
return redirect($response, $request->getUri());
}
$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
if ($ret === false) {
$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
return redirect($response, './');
return redirect($response, $request->getUri());
}
}
@ -269,7 +270,7 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
migrate($config);
} catch (PDOException $exception) {
$this->session->alert("Cannot connect to the database: {$exception->getMessage()} [{$exception->getCode()}]", 'danger');
return redirect($response, './');
return redirect($response, $request->getUri());
}
// if not installed, create the default admin account
@ -281,7 +282,7 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
cleanDirectory(__DIR__ . '/../resources/cache');
cleanDirectory(__DIR__ . '/../resources/sessions');
removeDirectory(__DIR__ . '/../install');
//removeDirectory(__DIR__ . '/../install');
// if is upgrading and existing installation, put it out maintenance
if ($installed) {
@ -290,7 +291,7 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
if ($ret === false) {
$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
return redirect($response, './');
return redirect($response, $request->getUri());
}
}

View file

@ -1,17 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<title>Installing XBackBone</title>
<title>XBackBone Installer</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="A lightweight PHP backend for ShareX">
<meta name="description" content="XBackBone Installer">
<link href="{{ request.uri }}../static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ request.uri }}../static/app/app.css" rel="stylesheet">
<link href="../static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/app/app.css" rel="stylesheet">
<script src="{{ request.uri }}../static/jquery/jquery.min.js"></script>
<script src="{{ request.uri }}../static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="{{ request.uri }}../static/fontawesome/js/all.min.js"></script>
<script src="../static/jquery/jquery.min.js"></script>
<script src="../static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="../static/fontawesome/js/all.min.js"></script>
<script src="installer.js"></script>
</head>
<body>
<div class="container">
@ -50,25 +51,25 @@
</div>
</div>
<div class="form-group row">
<div class="form-group row hook-database">
<label for="db_user" class="col-sm-3 col-form-label">Database Username</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="db_user" name="db_user" autocomplete="off" disabled>
<input type="text" class="form-control hook-database-input" id="db_user" name="db_user" value="db_user" autocomplete="off">
</div>
</div>
<div class="form-group row">
<div class="form-group row hook-database">
<label for="db_password" class="col-sm-3 col-form-label">Database Password</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="db_password" name="db_password" autocomplete="off" disabled>
<input type="password" class="form-control hook-database-input" id="db_password" name="db_password" autocomplete="off">
</div>
</div>
<hr>
<div class="form-group row">
<label for="storage_driver" class="col-sm-3 col-form-label">Storage Driver</label>
<div class="col-sm-9">
<select id="storage_driver" name="storage_driver">
<option value="local" selected>Local Storage</option>
<select id="storage_driver" name="storage_driver" class="form-control" required>
<option value="local">Local Storage</option>
<option value="ftp">FTP/FTPS</option>
<option value="s3">Amazon AWS S3</option>
<option value="google-cloud">Google Cloud Storage</option>
@ -76,94 +77,94 @@
</select>
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_path" class="col-sm-3 col-form-label">Storage path root</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_path" name="storage_path" value="./storage" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_path" name="storage_path" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_key" class="col-sm-3 col-form-label">AWS S3 key</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_key" name="storage_key" placeholder="your-key" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_key" name="storage_key" placeholder="your-key" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_secret" class="col-sm-3 col-form-label">AWS S3 secret</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_secret" name="storage_secret" placeholder="your-secret" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_secret" name="storage_secret" placeholder="your-secret" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_region" class="col-sm-3 col-form-label">AWS S3 region</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_region" name="storage_region" placeholder="your-region" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_region" name="storage_region" placeholder="your-region" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_token" class="col-sm-3 col-form-label">Dropbox token</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_token" name="storage_token" placeholder="authorization-token" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_token" name="storage_token" placeholder="authorization-token" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_host" class="col-sm-3 col-form-label">FTP host</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_host" name="storage_host" placeholder="127.0.0.1" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_host" name="storage_host" placeholder="127.0.0.1" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_username" class="col-sm-3 col-form-label">FTP username</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_username" name="storage_username" placeholder="username" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_username" name="storage_username" placeholder="username" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_password" class="col-sm-3 col-form-label">FTP password</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="storage_password" name="storage_password" autocomplete="off">
<input type="password" class="form-control hook-storage-input" id="storage_password" name="storage_password" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_port" class="col-sm-3 col-form-label">FTP port</label>
<div class="col-sm-9">
<input type="number" min="0" max="65535" class="form-control" id="storage_port" name="storage_port" placeholder="21" autocomplete="off">
<input type="number" min="0" max="65535" class="form-control hook-storage-input" id="storage_port" name="storage_port" placeholder="21" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_passive" class="col-sm-3 col-form-label">FTP passive mode</label>
<div class="col-sm-9">
<select name="storage_passive" id="storage_passive">
<select name="storage_passive" id="storage_passive" class="form-control hook-storage-input">
<option value="1" selected>Yes</option>
<option value="0">No</option>
</select>
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_ssl" class="col-sm-3 col-form-label">FTP SSL enabled</label>
<div class="col-sm-9">
<select name="storage_ssl" id="storage_ssl">
<select name="storage_ssl" id="storage_ssl" class="form-control hook-storage-input">
<option value="1">Yes</option>
<option value="0" selected>No</option>
</select>
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_project_id" class="col-sm-3 col-form-label">Google project id</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_project_id" name="storage_project_id" placeholder="your-project-id" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_project_id" name="storage_project_id" placeholder="your-project-id" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_key_path" class="col-sm-3 col-form-label">Google key path</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_key_path" name="storage_key_path" placeholder="/path/to/service-account.json" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_key_path" name="storage_key_path" placeholder="/path/to/service-account.json" autocomplete="off">
</div>
</div>
<div class="form-group row hide-hook">
<div class="form-group row hook-storage">
<label for="storage_bucket" class="col-sm-3 col-form-label">Storage bucket</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="storage_bucket" name="storage_bucket" placeholder="your-bucket-name" autocomplete="off">
<input type="text" class="form-control hook-storage-input" id="storage_bucket" name="storage_bucket" placeholder="your-bucket-name" autocomplete="off">
</div>
</div>
<hr>
@ -183,8 +184,8 @@
<div class="form-group row justify-content-md-end">
<div class="col-sm-9">
<button type="submit" class="btn btn-outline-success">
<i class="fas fa-save fa-fw"></i> Configure & Install
<button type="submit" class="btn btn-outline-success" onsubmit="$('#modalLoading').modal({backdrop: 'static', keyboard: false})">
<i class="fas fa-save fa-fw"></i> Install
</button>
</div>
</div>
@ -192,7 +193,7 @@
{% else %}
<div class="form-group row">
<div class="col-sm-12 d-flex justify-content-center">
<button type="submit" class="btn btn-lg btn-outline-primary">
<button type="submit" class="btn btn-lg btn-outline-primary" onsubmit="$('#modalLoading').modal({backdrop: 'static', keyboard: false})">
<i class="fas fa-sync fa-fw"></i> Update database
</button>
</div>
@ -204,26 +205,17 @@
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#connection').change(function () {
switch ($(this).val()) {
case 'sqlite':
$('#dsn').val('resources/database/xbackbone.db');
$('#db_user').val('').prop('disabled', 'disabled');
$('#db_password').val('').prop('disabled', 'disabled');
break;
case 'mysql':
$('#dsn').val('host=localhost;port=3306;dbname=xbackbone');
$('#db_user').val('db_user').prop('disabled', '');
$('#db_password').val('').prop('disabled', '');
break;
}
});
$('#storage_driver').change(function () {
})
})
</script>
<div class="modal fade" id="modalLoading" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title">Please wait...</h6>
</div>
<div class="modal-body text-center">
<i class="fas fa-10x fa-spinner fa-pulse"></i>
</div>
</div>
</div>
</div>
</body>
</html>

722
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"dependencies": {
"@fortawesome/fontawesome-free": "^5.8.1",
"@fortawesome/fontawesome-free": "^5.8.2",
"bootstrap": "^4.3.1",
"clipboard": "^2.0.4",
"highlightjs": "^9.12.0",

63
src/js/installer.js Normal file
View file

@ -0,0 +1,63 @@
$(document).ready(function () {
var $connection = $('#connection');
var $all_database_options = $('.hook-database');
var $all_database_inputs = $('.hook-database-input');
var $storage_driver = $('#storage_driver');
var $all_storage_inputs = $('.hook-storage-input');
var $all_storage_options = $('.hook-storage');
$connection.change(function () {
$all_database_options.hide();
$all_database_inputs.prop('required', '');
switch ($(this).val()) {
case 'sqlite':
$('#dsn').val('resources/database/xbackbone.db');
break;
case 'mysql':
$('#dsn').val('host=localhost;port=3306;dbname=xbackbone');
$('#db_user').prop('required', 'required').parent().parent().show();
$('#db_password').prop('required', 'required').parent().parent().show();
break;
}
});
$storage_driver.change(function () {
$all_storage_options.hide();
$all_storage_inputs.prop('required', '');
switch ($(this).val()) {
case 'local':
$('#storage_path').val('./storage').prop('required', 'required').parent().parent().show();
break;
case 'ftp':
$('#storage_path').val('/storage').prop('required', 'required').parent().parent().show();
$('#storage_host').prop('required', 'required').parent().parent().show();
$('#storage_username').prop('required', 'required').parent().parent().show();
$('#storage_password').prop('required', 'required').parent().parent().show();
$('#storage_port').prop('required', 'required').parent().parent().show();
$('#storage_passive').prop('required', 'required').parent().parent().show();
$('#storage_ssl').prop('required', 'required').parent().parent().show();
break;
case 's3':
$('#storage_path').val('/storage').prop('required', 'required').parent().parent().show();
$('#storage_key').prop('required', 'required').parent().parent().show();
$('#storage_secret').prop('required', 'required').parent().parent().show();
$('#storage_region').prop('required', 'required').parent().parent().show();
$('#storage_bucket').prop('required', 'required').parent().parent().show();
break;
case 'google-cloud':
$('#storage_project_id').prop('required', 'required').parent().parent().show();
$('#storage_key_path').prop('required', 'required').parent().parent().show();
$('#storage_bucket').prop('required', 'required').parent().parent().show();
break;
case 'dropbox':
$('#storage_token').prop('required', 'required').parent().parent().show();
break;
}
});
$all_database_options.hide();
$all_storage_options.hide();
$storage_driver.trigger('change');
$connection.trigger('change');
});