Working on install wizard
This commit is contained in:
parent
ca9b8b5bc6
commit
72523843b6
9 changed files with 291 additions and 16 deletions
|
@ -91,9 +91,7 @@ class DashboardController extends Controller
|
||||||
$totalSize += $filesystem->getSize($media->storage_path);
|
$totalSize += $filesystem->getSize($media->storage_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->view->render(
|
return $this->view->render($response, 'dashboard/system.twig', [
|
||||||
$response,
|
|
||||||
'dashboard/system.twig', [
|
|
||||||
'usersCount' => $usersCount,
|
'usersCount' => $usersCount,
|
||||||
'mediasCount' => $mediasCount,
|
'mediasCount' => $mediasCount,
|
||||||
'orphanFilesCount' => $orphanFilesCount,
|
'orphanFilesCount' => $orphanFilesCount,
|
||||||
|
|
|
@ -8,13 +8,15 @@ class Session
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a session if is not already started in the current context
|
* Start a session if is not already started in the current context
|
||||||
|
* @param string $name
|
||||||
|
* @param string $path
|
||||||
*/
|
*/
|
||||||
public static function init(): void
|
public static function init(string $name, $path = ''): void
|
||||||
{
|
{
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
session_start([
|
session_start([
|
||||||
'name' => 'xbackbone_session',
|
'name' => $name,
|
||||||
'save_path' => 'resources/sessions'
|
'save_path' => $path
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,6 @@ class Session
|
||||||
*/
|
*/
|
||||||
public static function clear(): void
|
public static function clear(): void
|
||||||
{
|
{
|
||||||
self::init();
|
|
||||||
$_SESSION = [];
|
$_SESSION = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +45,6 @@ class Session
|
||||||
*/
|
*/
|
||||||
public static function has($key): bool
|
public static function has($key): bool
|
||||||
{
|
{
|
||||||
self::init();
|
|
||||||
return isset($_SESSION[$key]);
|
return isset($_SESSION[$key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ class Session
|
||||||
*/
|
*/
|
||||||
public static function all(): array
|
public static function all(): array
|
||||||
{
|
{
|
||||||
self::init();
|
|
||||||
return $_SESSION;
|
return $_SESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +65,6 @@ class Session
|
||||||
*/
|
*/
|
||||||
public static function get($key, $default = null)
|
public static function get($key, $default = null)
|
||||||
{
|
{
|
||||||
self::init();
|
|
||||||
return self::has($key) ? $_SESSION[$key] : $default;
|
return self::has($key) ? $_SESSION[$key] : $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +75,6 @@ class Session
|
||||||
*/
|
*/
|
||||||
public static function set($key, $value): void
|
public static function set($key, $value): void
|
||||||
{
|
{
|
||||||
self::init();
|
|
||||||
$_SESSION[$key] = $value;
|
$_SESSION[$key] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +85,6 @@ class Session
|
||||||
*/
|
*/
|
||||||
public static function alert($message, string $type = 'info'): void
|
public static function alert($message, string $type = 'info'): void
|
||||||
{
|
{
|
||||||
self::init();
|
|
||||||
$_SESSION['_flash'] = [$type => $message];
|
$_SESSION['_flash'] = [$type => $message];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ $container['logger'] = function ($container) {
|
||||||
return $logger;
|
return $logger;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Session init
|
||||||
|
Session::init('xbackbone_session', 'resources/sessions');
|
||||||
|
|
||||||
// Set the database dsn
|
// Set the database dsn
|
||||||
DB::setDsn($config['db']['connection'] . ':' . $config['db']['dsn'], $config['db']['username'], $config['db']['password']);
|
DB::setDsn($config['db']['connection'] . ':' . $config['db']['dsn'], $config['db']['username'], $config['db']['password']);
|
||||||
|
@ -76,6 +78,7 @@ $container['errorHandler'] = function ($container) {
|
||||||
return $container->view->render($response->withStatus(500), 'errors/500.twig', ['exception' => $exception]);
|
return $container->view->render($response->withStatus(500), 'errors/500.twig', ['exception' => $exception]);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
$container['notFoundHandler'] = function ($container) {
|
$container['notFoundHandler'] = function ($container) {
|
||||||
return function (\Slim\Http\Request $request, \Slim\Http\Response $response) use (&$container) {
|
return function (\Slim\Http\Request $request, \Slim\Http\Response $response) use (&$container) {
|
||||||
$response->withStatus(404)->withHeader('Content-Type', 'text/html');
|
$response->withStatus(404)->withHeader('Content-Type', 'text/html');
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
require 'vendor/autoload.php';
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
define('PLATFORM_VERSION', json_decode(file_get_contents('composer.json'))->version);
|
define('PLATFORM_VERSION', json_decode(file_get_contents('composer.json'))->version);
|
||||||
|
|
||||||
|
|
139
install/index.php
Normal file
139
install/index.php
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
<?php
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use App\Database\DB;
|
||||||
|
use App\Web\Session;
|
||||||
|
use Slim\App;
|
||||||
|
use Slim\Container;
|
||||||
|
use Slim\Http\Request;
|
||||||
|
use Slim\Http\Response;
|
||||||
|
|
||||||
|
define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer.json'))->version);
|
||||||
|
|
||||||
|
$config = [
|
||||||
|
'app_name' => 'XBackBone',
|
||||||
|
'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
|
||||||
|
'storage_dir' => 'storage',
|
||||||
|
'displayErrorDetails' => false,
|
||||||
|
'db' => [
|
||||||
|
'connection' => 'sqlite',
|
||||||
|
'dsn' => 'resources/database/xbackbone.db',
|
||||||
|
'username' => null,
|
||||||
|
'password' => null,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$container = new Container(['settings' => $config]);
|
||||||
|
|
||||||
|
Session::init('xbackbone_session');
|
||||||
|
|
||||||
|
$container['view'] = function ($container) use (&$config) {
|
||||||
|
$view = new \Slim\Views\Twig('templates', [
|
||||||
|
'cache' => false,
|
||||||
|
'autoescape' => 'html',
|
||||||
|
'debug' => $config['displayErrorDetails'],
|
||||||
|
'auto_reload' => $config['displayErrorDetails'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Instantiate and add Slim specific extension
|
||||||
|
$router = $container->get('router');
|
||||||
|
$uri = \Slim\Http\Uri::createFromEnvironment(new \Slim\Http\Environment($_SERVER));
|
||||||
|
$view->addExtension(new Slim\Views\TwigExtension($router, $uri));
|
||||||
|
|
||||||
|
$view->getEnvironment()->addGlobal('config', $config);
|
||||||
|
$view->getEnvironment()->addGlobal('request', $container->get('request'));
|
||||||
|
$view->getEnvironment()->addGlobal('alerts', Session::getAlert());
|
||||||
|
$view->getEnvironment()->addGlobal('session', Session::all());
|
||||||
|
$view->getEnvironment()->addGlobal('PLATFORM_VERSION', PLATFORM_VERSION);
|
||||||
|
return $view;
|
||||||
|
};
|
||||||
|
|
||||||
|
$app = new App($container);
|
||||||
|
|
||||||
|
$app->get('/', function (Request $request, Response $response) {
|
||||||
|
return $this->view->render($response, 'install.twig');
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->post('/', function (Request $request, Response $response) use (&$config) {
|
||||||
|
$config['base_url'] = $request->getParam('base_url');
|
||||||
|
$config['storage_dir'] = $request->getParam('storage_dir');
|
||||||
|
$config['db']['connection'] = $request->getParam('connection');
|
||||||
|
$config['db']['dsn'] = $request->getParam('dsn');
|
||||||
|
$config['db']['username'] = null;
|
||||||
|
$config['db']['password'] = null;
|
||||||
|
|
||||||
|
|
||||||
|
file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
|
||||||
|
|
||||||
|
|
||||||
|
DB::setDsn($config['db']['connection'] . ':' . __DIR__ . '../' . $config['db']['dsn'], $config['db']['username'], $config['db']['password']);
|
||||||
|
|
||||||
|
$firstMigrate = false;
|
||||||
|
if (!file_exists(__DIR__ . '../' . $config['db']['dsn']) && DB::driver() === 'sqlite') {
|
||||||
|
touch(__DIR__ . '../' . $config['db']['dsn']);
|
||||||
|
$firstMigrate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::query('SELECT 1 FROM `migrations` LIMIT 1');
|
||||||
|
} catch (PDOException $exception) {
|
||||||
|
$firstMigrate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo 'Connected.' . PHP_EOL;
|
||||||
|
|
||||||
|
if ($firstMigrate) {
|
||||||
|
echo 'Creating migrations table...' . PHP_EOL;
|
||||||
|
DB::raw()->exec(file_get_contents(__DIR__ . '../resources/schemas/migrations.sql'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = glob(__DIR__ . '../resources/schemas/' . DB::driver() . '/*.sql');
|
||||||
|
|
||||||
|
$names = array_map(function ($path) {
|
||||||
|
return basename($path);
|
||||||
|
}, $files);
|
||||||
|
|
||||||
|
$in = str_repeat('?, ', count($names) - 1) . '?';
|
||||||
|
|
||||||
|
$inMigrationsTable = DB::query("SELECT * FROM `migrations` WHERE `name` IN ($in)", $names)->fetchAll();
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
|
||||||
|
$continue = false;
|
||||||
|
$exists = false;
|
||||||
|
|
||||||
|
foreach ($inMigrationsTable as $migration) {
|
||||||
|
if (basename($file) === $migration->name && $migration->migrated) {
|
||||||
|
$continue = true;
|
||||||
|
break;
|
||||||
|
} elseif (basename($file) === $migration->name && !$migration->migrated) {
|
||||||
|
$exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($continue) continue;
|
||||||
|
|
||||||
|
$sql = file_get_contents($file);
|
||||||
|
try {
|
||||||
|
DB::raw()->exec($sql);
|
||||||
|
if (!$exists) {
|
||||||
|
DB::query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 1]);
|
||||||
|
} else {
|
||||||
|
DB::query('UPDATE `migrations` SET `migrated`=? WHERE `name`=?', [1, basename($file)]);
|
||||||
|
}
|
||||||
|
} catch (PDOException $exception) {
|
||||||
|
if (!$exists) {
|
||||||
|
DB::query('INSERT INTO `migrations` VALUES (?,?)', [basename($file), 0]);
|
||||||
|
}
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::query("INSERT INTO `users` (`email`, `username`, `password`, `is_admin`, `user_code`) VALUES (?, 'admin', ?, 1, ?)", [$request->getParam('email'), password_hash($request->getParam('password'), PASSWORD_DEFAULT), substr(md5(microtime()), rand(0, 26), 5)]);
|
||||||
|
|
||||||
|
Session::alert('Installation completed successfully!', 'success');
|
||||||
|
return $response->withRedirect('../?afterInstall=true');
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
8
install/templates/alert.twig
Normal file
8
install/templates/alert.twig
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% for type, message in alerts %}
|
||||||
|
<div class="alert alert-{{ type }} alert-dismissible fade show" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
7
install/templates/footer.twig
Normal file
7
install/templates/footer.twig
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="text-muted">Proudly powered by
|
||||||
|
<a href="https://github.com/SergiX44/XBackBone">XBackBone v{{ PLATFORM_VERSION }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
124
install/templates/install.twig
Normal file
124
install/templates/install.twig
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Installing XBackBone | {{ config.app_name }}</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">
|
||||||
|
|
||||||
|
<link href="{{ config.base_url }}/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="{{ config.base_url }}/static/highlightjs/styles/monokai.css" rel="stylesheet">
|
||||||
|
<link href="{{ config.base_url }}/static/app/app.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="{{ config.base_url }}/static/jquery/jquery.min.js"></script>
|
||||||
|
<script src="{{ config.base_url }}/static/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="{{ config.base_url }}/static/fontawesome/js/all.min.js"></script>
|
||||||
|
<script src="{{ config.base_url }}/static/highlightjs/highlight.pack.min.js"></script>
|
||||||
|
<script src="{{ config.base_url }}/static/clipboardjs/clipboard.min.js"></script>
|
||||||
|
<script src="{{ config.base_url }}/static/app/app.js"></script>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
{% include 'alert.twig' %}
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card mt-3">
|
||||||
|
<div class="card-header">Install XBackBone</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" action="">
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="base_url" class="col-sm-3 col-form-label">Base URL</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" id="base_url" name="base_url" value="{{ config.base_url }}" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="connection" class="col-sm-3 col-form-label">SQL Engine</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select name="connection" id="connection" required class="form-control">
|
||||||
|
<option value="sqlite" selected>SQLite</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="dsn" class="col-sm-3 col-form-label">Database Source Name (DSN)</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" id="dsn" name="dsn" value="{{ config.db.dsn }}" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="storage_dir" class="col-sm-3 col-form-label">Storage Directory</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" id="storage_dir" name="storage_dir" value="{{ config.storage_dir }}" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="email" class="col-sm-3 col-form-label">Admin email</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="email" class="form-control" id="email" placeholder="email@example.com" name="email" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="password" class="col-sm-3 col-form-label">Admin password</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="password" class="form-control" id="password" placeholder="Password" name="password" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'footer.twig' %}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -7,18 +7,18 @@
|
||||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{{ config.base_url }}/home" class="nav-link {{ request.url starts with '/home' ? 'active' }}"><i class="fas fa-fw fa-home"></i>
|
<a href="{{ config.base_url }}/home" class="nav-link {{ request.uri.path starts with '/home' ? 'active' }}"><i class="fas fa-fw fa-home"></i>
|
||||||
Home
|
Home
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if session.admin %}
|
{% if session.admin %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{{ config.base_url }}/users" class="nav-link {{ request.url starts with '/user' ? 'active' }}"><i class="fas fa-fw fa-users"></i>
|
<a href="{{ config.base_url }}/users" class="nav-link {{ request.uri.path starts with '/user' ? 'active' }}"><i class="fas fa-fw fa-users"></i>
|
||||||
Users
|
Users
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{{ config.base_url }}/system" class="nav-link {{ request.url starts with '/system' ? 'active' }}"><i class="fas fa-fw fa-cog"></i>
|
<a href="{{ config.base_url }}/system" class="nav-link {{ request.uri.path starts with '/system' ? 'active' }}"><i class="fas fa-fw fa-cog"></i>
|
||||||
System
|
System
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in a new issue