소스 검색

Working on install wizard

Sergio Brighenti 6 년 전
부모
커밋
72523843b6

+ 1 - 3
app/Controllers/DashboardController.php

@@ -91,9 +91,7 @@ class DashboardController extends Controller
 			$totalSize += $filesystem->getSize($media->storage_path);
 		}
 
-		return $this->view->render(
-			$response,
-			'dashboard/system.twig', [
+		return $this->view->render($response, 'dashboard/system.twig', [
 			'usersCount' => $usersCount,
 			'mediasCount' => $mediasCount,
 			'orphanFilesCount' => $orphanFilesCount,

+ 5 - 9
app/Web/Session.php

@@ -8,13 +8,15 @@ class Session
 
 	/**
 	 * 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) {
 			session_start([
-				'name' => 'xbackbone_session',
-				'save_path' => 'resources/sessions'
+				'name' => $name,
+				'save_path' => $path
 			]);
 		}
 	}
@@ -33,7 +35,6 @@ class Session
 	 */
 	public static function clear(): void
 	{
-		self::init();
 		$_SESSION = [];
 	}
 
@@ -44,7 +45,6 @@ class Session
 	 */
 	public static function has($key): bool
 	{
-		self::init();
 		return isset($_SESSION[$key]);
 	}
 
@@ -54,7 +54,6 @@ class Session
 	 */
 	public static function all(): array
 	{
-		self::init();
 		return $_SESSION;
 	}
 
@@ -66,7 +65,6 @@ class Session
 	 */
 	public static function get($key, $default = null)
 	{
-		self::init();
 		return self::has($key) ? $_SESSION[$key] : $default;
 	}
 
@@ -77,7 +75,6 @@ class Session
 	 */
 	public static function set($key, $value): void
 	{
-		self::init();
 		$_SESSION[$key] = $value;
 	}
 
@@ -88,7 +85,6 @@ class Session
 	 */
 	public static function alert($message, string $type = 'info'): void
 	{
-		self::init();
 		$_SESSION['_flash'] = [$type => $message];
 	}
 

+ 3 - 0
bootstrap/app.php

@@ -35,6 +35,8 @@ $container['logger'] = function ($container) {
 	return $logger;
 };
 
+// Session init
+Session::init('xbackbone_session', 'resources/sessions');
 
 // Set the database dsn
 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]);
 	};
 };
+
 $container['notFoundHandler'] = function ($container) {
 	return function (\Slim\Http\Request $request, \Slim\Http\Response $response) use (&$container) {
 		$response->withStatus(404)->withHeader('Content-Type', 'text/html');

+ 1 - 1
index.php

@@ -1,5 +1,5 @@
 <?php
-require 'vendor/autoload.php';
+require __DIR__ . '/vendor/autoload.php';
 
 define('PLATFORM_VERSION', json_decode(file_get_contents('composer.json'))->version);
 

+ 139 - 0
install/index.php

@@ -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 - 0
install/templates/alert.twig

@@ -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">&times;</span>
+        </button>
+    </div>
+{% endfor %}

+ 7 - 0
install/templates/footer.twig

@@ -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 - 0
install/templates/install.twig

@@ -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>

+ 3 - 3
resources/templates/comp/navbar.twig

@@ -7,18 +7,18 @@
         <div class="collapse navbar-collapse" id="navbarCollapse">
             <ul class="navbar-nav mr-auto">
                 <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
                     </a>
                 </li>
                 {% if session.admin %}
                     <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
                         </a>
                     </li>
                     <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
                         </a>
                     </li>