浏览代码

Start adding support to use AWS S3, Google Cloud Storage, Dropbox and FTP(s) accounts as storage location

Sergio Brighenti 6 年之前
父节点
当前提交
f6a6be7312

+ 4 - 0
CHANGELOG.md

@@ -1,3 +1,7 @@
+## v2.6
++ Added support to use AWS S3, Google Cloud Storage, Dropbox and FTP(s) accounts as storage location.
++ Fixed missing icon. 
+
 ## v2.5.3
 ## v2.5.3
 + Fixed bad css loading on Firefox (#35).
 + Fixed bad css loading on Firefox (#35).
 + Fixed wrong style for publish/unpublish button.
 + Fixed wrong style for publish/unpublish button.

+ 2 - 2
app/Controllers/AdminController.php

@@ -26,7 +26,7 @@ class AdminController extends Controller
 
 
 		$totalSize = 0;
 		$totalSize = 0;
 
 
-		$filesystem = storage();
+		$filesystem = $this->storage;
 		foreach ($medias as $media) {
 		foreach ($medias as $media) {
 			$totalSize += $filesystem->getSize($media->storage_path);
 			$totalSize += $filesystem->getSize($media->storage_path);
 		}
 		}
@@ -50,7 +50,7 @@ class AdminController extends Controller
 	{
 	{
 		$orphans = $this->database->query('SELECT * FROM `uploads` WHERE `user_id` IS NULL')->fetchAll();
 		$orphans = $this->database->query('SELECT * FROM `uploads` WHERE `user_id` IS NULL')->fetchAll();
 
 
-		$filesystem = storage();
+		$filesystem = $this->storage;
 		$deleted = 0;
 		$deleted = 0;
 
 
 		foreach ($orphans as $orphan) {
 		foreach ($orphans as $orphan) {

+ 3 - 1
app/Controllers/Controller.php

@@ -5,6 +5,7 @@ namespace App\Controllers;
 use App\Database\DB;
 use App\Database\DB;
 use App\Web\Session;
 use App\Web\Session;
 use League\Flysystem\FileNotFoundException;
 use League\Flysystem\FileNotFoundException;
+use League\Flysystem\Filesystem;
 use Monolog\Logger;
 use Monolog\Logger;
 use Slim\Container;
 use Slim\Container;
 
 
@@ -13,6 +14,7 @@ use Slim\Container;
  * @property mixed|null view
  * @property mixed|null view
  * @property DB|null database
  * @property DB|null database
  * @property Logger|null logger
  * @property Logger|null logger
+ * @property Filesystem|null storage
  */
  */
 abstract class Controller
 abstract class Controller
 {
 {
@@ -48,7 +50,7 @@ abstract class Controller
 
 
 		$totalSize = 0;
 		$totalSize = 0;
 
 
-		$filesystem = storage();
+		$filesystem = $this->storage;
 		foreach ($medias as $media) {
 		foreach ($medias as $media) {
 			try {
 			try {
 				$totalSize += $filesystem->getSize($media->storage_path);
 				$totalSize += $filesystem->getSize($media->storage_path);

+ 1 - 1
app/Controllers/DashboardController.php

@@ -34,7 +34,7 @@ class DashboardController extends Controller
 		$page = isset($args['page']) ? (int)$args['page'] : 0;
 		$page = isset($args['page']) ? (int)$args['page'] : 0;
 		$page = max(0, --$page);
 		$page = max(0, --$page);
 
 
-		$query = new MediaQuery($this->database, $this->session->get('admin', false));
+		$query = new MediaQuery($this->database, $this->session->get('admin', false), $this->storage);
 
 
 		switch ($request->getParam('sort', 'time')) {
 		switch ($request->getParam('sort', 'time')) {
 			case 'size':
 			case 'size':

+ 7 - 7
app/Controllers/UploadController.php

@@ -71,7 +71,7 @@ class UploadController extends Controller
 		$fileInfo = pathinfo($file->getClientFilename());
 		$fileInfo = pathinfo($file->getClientFilename());
 		$storagePath = "$user->user_code/$code.$fileInfo[extension]";
 		$storagePath = "$user->user_code/$code.$fileInfo[extension]";
 
 
-		storage()->writeStream($storagePath, $file->getStream()->detach());
+		$this->storage->writeStream($storagePath, $file->getStream()->detach());
 
 
 		$this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`) VALUES (?, ?, ?, ?)', [
 		$this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`) VALUES (?, ?, ?, ?)', [
 			$user->id,
 			$user->id,
@@ -104,7 +104,7 @@ class UploadController extends Controller
 			throw new NotFoundException($request, $response);
 			throw new NotFoundException($request, $response);
 		}
 		}
 
 
-		$filesystem = storage();
+		$filesystem = $this->storage;
 
 
 		if (isBot($request->getHeaderLine('User-Agent'))) {
 		if (isBot($request->getHeaderLine('User-Agent'))) {
 			return $this->streamMedia($request, $response, $filesystem, $media);
 			return $this->streamMedia($request, $response, $filesystem, $media);
@@ -168,7 +168,7 @@ class UploadController extends Controller
 		if ($this->session->get('admin', false) || $user->id === $media->user_id) {
 		if ($this->session->get('admin', false) || $user->id === $media->user_id) {
 
 
 			try {
 			try {
-				storage()->delete($media->storage_path);
+				$this->storage->delete($media->storage_path);
 			} catch (FileNotFoundException $e) {
 			} catch (FileNotFoundException $e) {
 				throw new NotFoundException($request, $response);
 				throw new NotFoundException($request, $response);
 			} finally {
 			} finally {
@@ -199,7 +199,7 @@ class UploadController extends Controller
 			throw new NotFoundException($request, $response);
 			throw new NotFoundException($request, $response);
 		}
 		}
 
 
-		return $this->streamMedia($request, $response, storage(), $media);
+		return $this->streamMedia($request, $response, $this->storage, $media);
 	}
 	}
 
 
 	/**
 	/**
@@ -217,7 +217,7 @@ class UploadController extends Controller
 		if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) {
 		if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) {
 			throw new NotFoundException($request, $response);
 			throw new NotFoundException($request, $response);
 		}
 		}
-		return $this->streamMedia($request, $response, storage(), $media);
+		return $this->streamMedia($request, $response, $this->storage, $media);
 	}
 	}
 
 
 
 
@@ -236,7 +236,7 @@ class UploadController extends Controller
 		if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) {
 		if (!$media || !$media->published && $this->session->get('user_id') !== $media->user_id && !$this->session->get('admin', false)) {
 			throw new NotFoundException($request, $response);
 			throw new NotFoundException($request, $response);
 		}
 		}
-		return $this->streamMedia($request, $response, storage(), $media, 'attachment');
+		return $this->streamMedia($request, $response, $this->storage, $media, 'attachment');
 	}
 	}
 
 
 	/**
 	/**
@@ -282,7 +282,7 @@ class UploadController extends Controller
 		if ($this->session->get('admin', false) || $media->user_id === $this->session->get('user_id')) {
 		if ($this->session->get('admin', false) || $media->user_id === $this->session->get('user_id')) {
 
 
 			try {
 			try {
-				storage()->delete($media->storage_path);
+				$this->storage->delete($media->storage_path);
 			} catch (FileNotFoundException $e) {
 			} catch (FileNotFoundException $e) {
 				throw new NotFoundException($request, $response);
 				throw new NotFoundException($request, $response);
 			} finally {
 			} finally {

+ 13 - 9
app/Database/Queries/MediaQuery.php

@@ -5,6 +5,7 @@ namespace App\Database\Queries;
 
 
 use App\Database\DB;
 use App\Database\DB;
 use League\Flysystem\FileNotFoundException;
 use League\Flysystem\FileNotFoundException;
+use League\Flysystem\Filesystem;
 use League\Flysystem\Plugin\ListFiles;
 use League\Flysystem\Plugin\ListFiles;
 
 
 class MediaQuery
 class MediaQuery
@@ -34,6 +35,9 @@ class MediaQuery
 	/** @var string */
 	/** @var string */
 	protected $text;
 	protected $text;
 
 
+	/** @var Filesystem */
+	protected $storage;
+
 	private $pages;
 	private $pages;
 	private $media;
 	private $media;
 
 
@@ -41,11 +45,13 @@ class MediaQuery
 	 * MediaQuery constructor.
 	 * MediaQuery constructor.
 	 * @param DB $db
 	 * @param DB $db
 	 * @param bool $isAdmin
 	 * @param bool $isAdmin
+	 * @param Filesystem $storage
 	 */
 	 */
-	public function __construct(DB $db, bool $isAdmin)
+	public function __construct(DB $db, bool $isAdmin, Filesystem $storage)
 	{
 	{
 		$this->db = $db;
 		$this->db = $db;
 		$this->isAdmin = $isAdmin;
 		$this->isAdmin = $isAdmin;
+		$this->storage = $storage;
 	}
 	}
 
 
 	/**
 	/**
@@ -128,12 +134,10 @@ class MediaQuery
 			$this->pages = $this->db->query($queryPages, array_merge([$this->userId], $params))->fetch()->count / self::PER_PAGE;
 			$this->pages = $this->db->query($queryPages, array_merge([$this->userId], $params))->fetch()->count / self::PER_PAGE;
 		}
 		}
 
 
-		$filesystem = storage();
-
 		foreach ($this->media as $media) {
 		foreach ($this->media as $media) {
 			try {
 			try {
-				$media->size = humanFileSize($filesystem->getSize($media->storage_path));
-				$media->mimetype = $filesystem->getMimetype($media->storage_path);
+				$media->size = humanFileSize($this->storage->getSize($media->storage_path));
+				$media->mimetype = $this->storage->getMimetype($media->storage_path);
 			} catch (FileNotFoundException $e) {
 			} catch (FileNotFoundException $e) {
 				$media->size = null;
 				$media->size = null;
 				$media->mimetype = null;
 				$media->mimetype = null;
@@ -148,17 +152,17 @@ class MediaQuery
 	 */
 	 */
 	private function runWithOrderBySize(int $page)
 	private function runWithOrderBySize(int $page)
 	{
 	{
-		$filesystem = storage()->addPlugin(new ListFiles());
+		$this->storage->addPlugin(new ListFiles());
 
 
 		if ($this->isAdmin) {
 		if ($this->isAdmin) {
-			$files = $filesystem->listFiles('/', true);
+			$files = $this->storage->listFiles('/', true);
 			$this->pages = count($files) / self::PER_PAGE_ADMIN;
 			$this->pages = count($files) / self::PER_PAGE_ADMIN;
 
 
 			$offset = $page * self::PER_PAGE_ADMIN;
 			$offset = $page * self::PER_PAGE_ADMIN;
 			$limit = self::PER_PAGE_ADMIN;
 			$limit = self::PER_PAGE_ADMIN;
 		} else {
 		} else {
 			$userCode = $this->db->query('SELECT `user_code` FROM `users` WHERE `id` = ?', [$this->userId])->fetch()->user_code;
 			$userCode = $this->db->query('SELECT `user_code` FROM `users` WHERE `id` = ?', [$this->userId])->fetch()->user_code;
-			$files = $filesystem->listFiles($userCode);
+			$files = $this->storage->listFiles($userCode);
 			$this->pages = count($files) / self::PER_PAGE;
 			$this->pages = count($files) / self::PER_PAGE;
 
 
 			$offset = $page * self::PER_PAGE;
 			$offset = $page * self::PER_PAGE;
@@ -195,7 +199,7 @@ class MediaQuery
 			}
 			}
 			$media->size = humanFileSize($file['size']);
 			$media->size = humanFileSize($file['size']);
 			try {
 			try {
-				$media->mimetype = $filesystem->getMimetype($file['path']);
+				$media->mimetype = $this->storage->getMimetype($file['path']);
 			} catch (FileNotFoundException $e) {
 			} catch (FileNotFoundException $e) {
 				$media->mimetype = null;
 				$media->mimetype = null;
 			}
 			}

+ 2 - 16
app/helpers.php

@@ -5,20 +5,6 @@ use League\Flysystem\Filesystem;
 
 
 require __DIR__ . '/../vendor/autoload.php';
 require __DIR__ . '/../vendor/autoload.php';
 
 
-if (!function_exists('storage')) {
-	/**
-	 * Get a filesystem instance given a path
-	 * @param string $root
-	 * @return Filesystem
-	 */
-	function storage($root = null): Filesystem
-	{
-		global $app;
-		$storagePath = $app->getContainer()->get('settings')['storage_dir'];
-		return new Filesystem(new Local($root !== null ? $root : $storagePath));
-	}
-}
-
 if (!function_exists('humanFileSize')) {
 if (!function_exists('humanFileSize')) {
 	/**
 	/**
 	 * Generate a human readable file size
 	 * Generate a human readable file size
@@ -224,13 +210,13 @@ if (!function_exists('mime2font')) {
 				return $class;
 				return $class;
 			}
 			}
 		}
 		}
-		return 'fa-file-download';
+		return 'fa-file';
 	}
 	}
 }
 }
 
 
 if (!function_exists('dd')) {
 if (!function_exists('dd')) {
 	/**
 	/**
-	 * Dumps all the giver vars and halt the execution.
+	 * Dumps all the given vars and halt the execution.
 	 */
 	 */
 	function dd()
 	function dd()
 	{
 	{

+ 53 - 1
bootstrap/app.php

@@ -5,6 +5,12 @@ use App\Exceptions\MaintenanceException;
 use App\Exceptions\UnauthorizedException;
 use App\Exceptions\UnauthorizedException;
 use App\Web\Lang;
 use App\Web\Lang;
 use App\Web\Session;
 use App\Web\Session;
+use Aws\S3\S3Client;
+use Google\Cloud\Storage\StorageClient;
+use League\Flysystem\Adapter\Ftp as FtpAdapter;
+use League\Flysystem\Adapter\Local;
+use League\Flysystem\AwsS3v3\AwsS3Adapter;
+use League\Flysystem\Filesystem;
 use Monolog\Formatter\LineFormatter;
 use Monolog\Formatter\LineFormatter;
 use Monolog\Handler\RotatingFileHandler;
 use Monolog\Handler\RotatingFileHandler;
 use Monolog\Logger;
 use Monolog\Logger;
@@ -15,6 +21,9 @@ use Slim\Http\Request;
 use Slim\Http\Response;
 use Slim\Http\Response;
 use Slim\Http\Uri;
 use Slim\Http\Uri;
 use Slim\Views\Twig;
 use Slim\Views\Twig;
+use Spatie\Dropbox\Client as DropboxClient;
+use Spatie\FlysystemDropbox\DropboxAdapter;
+use Superbalist\Flysystem\GoogleStorage\GoogleStorageAdapter;
 use Twig\TwigFunction;
 use Twig\TwigFunction;
 
 
 if (!file_exists('config.php') && is_dir('install/')) {
 if (!file_exists('config.php') && is_dir('install/')) {
@@ -28,7 +37,6 @@ if (!file_exists('config.php') && is_dir('install/')) {
 $config = array_replace_recursive([
 $config = array_replace_recursive([
 	'app_name' => 'XBackBone',
 	'app_name' => 'XBackBone',
 	'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
 	'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
-	'storage_dir' => 'storage',
 	'displayErrorDetails' => false,
 	'displayErrorDetails' => false,
 	'maintenance' => false,
 	'maintenance' => false,
 	'db' => [
 	'db' => [
@@ -37,6 +45,9 @@ $config = array_replace_recursive([
 		'username' => null,
 		'username' => null,
 		'password' => null,
 		'password' => null,
 	],
 	],
+	'storage' => [
+		'driver' => 'local',
+	],
 ], require BASE_DIR . 'config.php');
 ], require BASE_DIR . 'config.php');
 
 
 if (!$config['displayErrorDetails']) {
 if (!$config['displayErrorDetails']) {
@@ -73,6 +84,47 @@ $container['database'] = function ($container) use (&$config) {
 	return new DB($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
 	return new DB($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
 };
 };
 
 
+$container['storage'] = function ($container) use (&$config) {
+
+	switch ($config['storage']['driver']) {
+		case 'local':
+			return new Filesystem(new Local($config['storage']['path']));
+		case 's3':
+			$client = new S3Client([
+				'credentials' => [
+					'key' => $config['storage']['key'],
+					'secret' => $config['storage']['secret'],
+				],
+				'region' => $config['storage']['region'],
+				'version' => 'latest',
+			]);
+
+			return new Filesystem(new AwsS3Adapter($client, $config['storage']['bucket'], $config['storage']['path']));
+		case 'dropbox':
+			$client = new DropboxClient($config['storage']['token']);
+			return new Filesystem(new DropboxAdapter($client), ['case_sensitive' => false]);
+		case 'ftp':
+			return new Filesystem(new FtpAdapter([
+				'host' =>  $config['storage']['host'],
+				'username' =>  $config['storage']['username'],
+				'password' =>  $config['storage']['password'],
+				'port' =>  $config['storage']['port'],
+				'root' =>  $config['storage']['path'],
+				'passive' =>  $config['storage']['passive'],
+				'ssl' =>  $config['storage']['ssl'],
+				'timeout' => 30,
+			]));
+		case 'google-cloud':
+			$client = new StorageClient([
+				'projectId' => $config['storage']['project_id'],
+				'keyFilePath' => $config['storage']['key_path'],
+			]);
+			return new Filesystem(new GoogleStorageAdapter($client, $client->bucket($config['storage']['bucket'])));
+		default:
+			throw new InvalidArgumentException('The driver specified is not supported.');
+	}
+};
+
 $container['lang'] = function ($container) {
 $container['lang'] = function ($container) {
 	return Lang::build(Lang::recognize(), BASE_DIR . 'resources/lang/');
 	return Lang::build(Lang::recognize(), BASE_DIR . 'resources/lang/');
 };
 };

+ 5 - 2
composer.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "sergix44/xbackbone",
   "name": "sergix44/xbackbone",
-  "version": "2.5.3",
+  "version": "2.6",
   "description": "A lightweight ShareX PHP backend",
   "description": "A lightweight ShareX PHP backend",
   "type": "project",
   "type": "project",
   "require": {
   "require": {
@@ -13,7 +13,10 @@
     "ext-json": "*",
     "ext-json": "*",
     "ext-gd": "*",
     "ext-gd": "*",
     "ext-pdo": "*",
     "ext-pdo": "*",
-    "ext-zip": "*"
+    "ext-zip": "*",
+    "league/flysystem-aws-s3-v3": "^1.0",
+    "spatie/flysystem-dropbox": "^1.0",
+    "superbalist/flysystem-google-storage": "^7.2"
   },
   },
   "autoload": {
   "autoload": {
     "files": [
     "files": [

+ 755 - 2
composer.lock

@@ -1,11 +1,94 @@
 {
 {
     "_readme": [
     "_readme": [
         "This file locks the dependencies of your project to a known state",
         "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"
         "This file is @generated automatically"
     ],
     ],
-    "content-hash": "120224d788960ba61ae3d0e49e173ad3",
+    "content-hash": "d2c8e7bde7729d78cef484491535c639",
     "packages": [
     "packages": [
+        {
+            "name": "aws/aws-sdk-php",
+            "version": "3.93.12",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/aws/aws-sdk-php.git",
+                "reference": "60f5c4bd261e19844d4263c3a8f370e88e038282"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/60f5c4bd261e19844d4263c3a8f370e88e038282",
+                "reference": "60f5c4bd261e19844d4263c3a8f370e88e038282",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-pcre": "*",
+                "ext-simplexml": "*",
+                "guzzlehttp/guzzle": "^5.3.3|^6.2.1",
+                "guzzlehttp/promises": "~1.0",
+                "guzzlehttp/psr7": "^1.4.1",
+                "mtdowling/jmespath.php": "~2.2",
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "andrewsville/php-token-reflection": "^1.4",
+                "aws/aws-php-sns-message-validator": "~1.0",
+                "behat/behat": "~3.0",
+                "doctrine/cache": "~1.4",
+                "ext-dom": "*",
+                "ext-openssl": "*",
+                "ext-pcntl": "*",
+                "ext-sockets": "*",
+                "nette/neon": "^2.3",
+                "phpunit/phpunit": "^4.8.35|^5.4.3",
+                "psr/cache": "^1.0",
+                "psr/simple-cache": "^1.0"
+            },
+            "suggest": {
+                "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications",
+                "doctrine/cache": "To use the DoctrineCacheAdapter",
+                "ext-curl": "To send requests using cURL",
+                "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages",
+                "ext-sockets": "To use client-side monitoring"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Aws\\": "src/"
+                },
+                "files": [
+                    "src/functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Amazon Web Services",
+                    "homepage": "http://aws.amazon.com"
+                }
+            ],
+            "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
+            "homepage": "http://aws.amazon.com/sdkforphp",
+            "keywords": [
+                "amazon",
+                "aws",
+                "cloud",
+                "dynamodb",
+                "ec2",
+                "glacier",
+                "s3",
+                "sdk"
+            ],
+            "time": "2019-05-17T18:26:46+00:00"
+        },
         {
         {
             "name": "container-interop/container-interop",
             "name": "container-interop/container-interop",
             "version": "1.2.0",
             "version": "1.2.0",
@@ -37,6 +120,329 @@
             "homepage": "https://github.com/container-interop/container-interop",
             "homepage": "https://github.com/container-interop/container-interop",
             "time": "2017-02-14T19:40:03+00:00"
             "time": "2017-02-14T19:40:03+00:00"
         },
         },
+        {
+            "name": "firebase/php-jwt",
+            "version": "v5.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/firebase/php-jwt.git",
+                "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
+                "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": " 4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Firebase\\JWT\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Neuman Vong",
+                    "email": "neuman+pear@twilio.com",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Anant Narayanan",
+                    "email": "anant@php.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+            "homepage": "https://github.com/firebase/php-jwt",
+            "time": "2017-06-27T22:17:23+00:00"
+        },
+        {
+            "name": "google/auth",
+            "version": "v1.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/googleapis/google-auth-library-php.git",
+                "reference": "0f75e20e7392e863f5550ed2c2d3d50af21710fb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/0f75e20e7392e863f5550ed2c2d3d50af21710fb",
+                "reference": "0f75e20e7392e863f5550ed2c2d3d50af21710fb",
+                "shasum": ""
+            },
+            "require": {
+                "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0",
+                "guzzlehttp/guzzle": "~5.3.1|~6.0",
+                "guzzlehttp/psr7": "^1.2",
+                "php": ">=5.4",
+                "psr/cache": "^1.0",
+                "psr/http-message": "^1.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^1.11",
+                "guzzlehttp/promises": "0.1.1|^1.3",
+                "phpseclib/phpseclib": "^2",
+                "phpunit/phpunit": "^4.8.36|^5.7",
+                "sebastian/comparator": ">=1.2.3"
+            },
+            "suggest": {
+                "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings. Please require version ^2."
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Google\\Auth\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "description": "Google Auth Library for PHP",
+            "homepage": "http://github.com/google/google-auth-library-php",
+            "keywords": [
+                "Authentication",
+                "google",
+                "oauth2"
+            ],
+            "time": "2019-04-16T18:48:28+00:00"
+        },
+        {
+            "name": "google/cloud-core",
+            "version": "v1.28.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/googleapis/google-cloud-php-core.git",
+                "reference": "d32db261499d28268f9ed97834e7aca829df5b53"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/googleapis/google-cloud-php-core/zipball/d32db261499d28268f9ed97834e7aca829df5b53",
+                "reference": "d32db261499d28268f9ed97834e7aca829df5b53",
+                "shasum": ""
+            },
+            "require": {
+                "google/auth": "^1.5.1",
+                "guzzlehttp/guzzle": "^5.3|^6.0",
+                "guzzlehttp/promises": "^1.3",
+                "guzzlehttp/psr7": "^1.2",
+                "monolog/monolog": "~1",
+                "php": ">=5.5",
+                "psr/http-message": "1.0.*",
+                "rize/uri-template": "~0.3"
+            },
+            "require-dev": {
+                "erusev/parsedown": "^1.6",
+                "google/gax": "^1.0",
+                "opis/closure": "^3",
+                "phpdocumentor/reflection": "^3.0",
+                "phpunit/phpunit": "^4.8|^5.0",
+                "squizlabs/php_codesniffer": "2.*"
+            },
+            "suggest": {
+                "opis/closure": "May be used to serialize closures to process jobs in the batch daemon. Please require version ^3.",
+                "symfony/lock": "Required for the Spanner cached based session pool. Please require the following commit: 3.3.x-dev#1ba6ac9"
+            },
+            "bin": [
+                "bin/google-cloud-batch"
+            ],
+            "type": "library",
+            "extra": {
+                "component": {
+                    "id": "cloud-core",
+                    "target": "googleapis/google-cloud-php-core.git",
+                    "path": "Core",
+                    "entry": "src/ServiceBuilder.php"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Google\\Cloud\\Core\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "description": "Google Cloud PHP shared dependency, providing functionality useful to all components.",
+            "time": "2019-04-16T20:44:33+00:00"
+        },
+        {
+            "name": "google/cloud-storage",
+            "version": "v1.12.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/googleapis/google-cloud-php-storage.git",
+                "reference": "a252e2012ea875e873b6d146159e54edb8fa4544"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/googleapis/google-cloud-php-storage/zipball/a252e2012ea875e873b6d146159e54edb8fa4544",
+                "reference": "a252e2012ea875e873b6d146159e54edb8fa4544",
+                "shasum": ""
+            },
+            "require": {
+                "google/cloud-core": "^1.28"
+            },
+            "require-dev": {
+                "erusev/parsedown": "^1.6",
+                "google/cloud-pubsub": "^1.0",
+                "phpdocumentor/reflection": "^3.0",
+                "phpseclib/phpseclib": "^2",
+                "phpunit/phpunit": "^4.8|^5.0",
+                "squizlabs/php_codesniffer": "2.*"
+            },
+            "suggest": {
+                "google/cloud-pubsub": "May be used to register a topic to receive bucket notifications.",
+                "phpseclib/phpseclib": "May be used in place of OpenSSL for creating signed Cloud Storage URLs. Please require version ^2."
+            },
+            "type": "library",
+            "extra": {
+                "component": {
+                    "id": "cloud-storage",
+                    "target": "googleapis/google-cloud-php-storage.git",
+                    "path": "Storage",
+                    "entry": "src/StorageClient.php"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Google\\Cloud\\Storage\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "description": "Cloud Storage Client for PHP",
+            "time": "2019-05-02T16:40:15+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "6.3.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/promises": "^1.0",
+                "guzzlehttp/psr7": "^1.4",
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+                "psr/log": "^1.0"
+            },
+            "suggest": {
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.3-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "time": "2018-04-22T15:46:56+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "v1.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "time": "2016-12-20T10:07:11+00:00"
+        },
         {
         {
             "name": "guzzlehttp/psr7",
             "name": "guzzlehttp/psr7",
             "version": "1.5.2",
             "version": "1.5.2",
@@ -258,6 +664,53 @@
             ],
             ],
             "time": "2019-03-30T13:22:34+00:00"
             "time": "2019-03-30T13:22:34+00:00"
         },
         },
+        {
+            "name": "league/flysystem-aws-s3-v3",
+            "version": "1.0.22",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
+                "reference": "883b02c80ca9cd68cf58a9b4b2185beef24b836b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/883b02c80ca9cd68cf58a9b4b2185beef24b836b",
+                "reference": "883b02c80ca9cd68cf58a9b4b2185beef24b836b",
+                "shasum": ""
+            },
+            "require": {
+                "aws/aws-sdk-php": "^3.0.0",
+                "league/flysystem": "^1.0.40",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "henrikbjorn/phpspec-code-coverage": "~1.0.1",
+                "phpspec/phpspec": "^2.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Flysystem\\AwsS3v3\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frank de Jonge",
+                    "email": "info@frenky.net"
+                }
+            ],
+            "description": "Flysystem adapter for the AWS S3 SDK v3.x",
+            "time": "2019-01-31T15:07:25+00:00"
+        },
         {
         {
             "name": "monolog/monolog",
             "name": "monolog/monolog",
             "version": "1.24.0",
             "version": "1.24.0",
@@ -336,6 +789,61 @@
             ],
             ],
             "time": "2018-11-05T09:00:11+00:00"
             "time": "2018-11-05T09:00:11+00:00"
         },
         },
+        {
+            "name": "mtdowling/jmespath.php",
+            "version": "2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/jmespath/jmespath.php.git",
+                "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/adcc9531682cf87dfda21e1fd5d0e7a41d292fac",
+                "reference": "adcc9531682cf87dfda21e1fd5d0e7a41d292fac",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
+            },
+            "bin": [
+                "bin/jp.php"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "JmesPath\\": "src/"
+                },
+                "files": [
+                    "src/JmesPath.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Declaratively specify how to extract elements from a JSON document",
+            "keywords": [
+                "json",
+                "jsonpath"
+            ],
+            "time": "2016-12-03T22:08:25+00:00"
+        },
         {
         {
             "name": "nikic/fast-route",
             "name": "nikic/fast-route",
             "version": "v1.3.0",
             "version": "v1.3.0",
@@ -432,6 +940,52 @@
             ],
             ],
             "time": "2018-01-21T07:42:36+00:00"
             "time": "2018-01-21T07:42:36+00:00"
         },
         },
+        {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "time": "2016-08-06T20:24:11+00:00"
+        },
         {
         {
             "name": "psr/container",
             "name": "psr/container",
             "version": "1.0.0",
             "version": "1.0.0",
@@ -618,6 +1172,50 @@
             "description": "A polyfill for getallheaders.",
             "description": "A polyfill for getallheaders.",
             "time": "2016-02-11T07:05:27+00:00"
             "time": "2016-02-11T07:05:27+00:00"
         },
         },
+        {
+            "name": "rize/uri-template",
+            "version": "0.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/rize/UriTemplate.git",
+                "reference": "9e5fdd5c47147aa5adf7f760002ee591ed37b9ca"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/rize/UriTemplate/zipball/9e5fdd5c47147aa5adf7f760002ee591ed37b9ca",
+                "reference": "9e5fdd5c47147aa5adf7f760002ee591ed37b9ca",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Rize\\UriTemplate": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marut K",
+                    "homepage": "http://twitter.com/rezigned"
+                }
+            ],
+            "description": "PHP URI Template (RFC 6570) supports both expansion & extraction",
+            "keywords": [
+                "RFC 6570",
+                "template",
+                "uri"
+            ],
+            "time": "2017-06-14T03:57:53+00:00"
+        },
         {
         {
             "name": "slim/slim",
             "name": "slim/slim",
             "version": "3.12.1",
             "version": "3.12.1",
@@ -740,6 +1338,161 @@
             ],
             ],
             "time": "2019-04-06T16:34:38+00:00"
             "time": "2019-04-06T16:34:38+00:00"
         },
         },
+        {
+            "name": "spatie/dropbox-api",
+            "version": "1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/dropbox-api.git",
+                "reference": "dc930cde4fdb802d20d0aeef5a98c3805fdcf9c2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/dropbox-api/zipball/dc930cde4fdb802d20d0aeef5a98c3805fdcf9c2",
+                "reference": "dc930cde4fdb802d20d0aeef5a98c3805fdcf9c2",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/guzzle": "^6.2",
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Spatie\\Dropbox\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Freek Van der Herten",
+                    "email": "freek@spatie.be",
+                    "homepage": "https://spatie.be",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Alex Vanderbist",
+                    "email": "alex.vanderbist@gmail.com",
+                    "homepage": "https://spatie.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A minimal implementation of Dropbox API v2",
+            "homepage": "https://github.com/spatie/dropbox-api",
+            "keywords": [
+                "Dropbox-API",
+                "api",
+                "dropbox",
+                "spatie",
+                "v2"
+            ],
+            "time": "2019-04-13T10:52:11+00:00"
+        },
+        {
+            "name": "spatie/flysystem-dropbox",
+            "version": "1.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/flysystem-dropbox.git",
+                "reference": "cff7dba925282736fff271fc41781a76fbe40799"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/flysystem-dropbox/zipball/cff7dba925282736fff271fc41781a76fbe40799",
+                "reference": "cff7dba925282736fff271fc41781a76fbe40799",
+                "shasum": ""
+            },
+            "require": {
+                "league/flysystem": "^1.0",
+                "php": "^7.0",
+                "spatie/dropbox-api": "^1.1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Spatie\\FlysystemDropbox\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Alex Vanderbist",
+                    "email": "alex.vanderbist@gmail.com",
+                    "homepage": "https://spatie.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Flysystem Adapter for the Dropbox v2 API",
+            "homepage": "https://github.com/spatie/flysystem-dropbox",
+            "keywords": [
+                "Flysystem",
+                "api",
+                "dropbox",
+                "flysystem-dropbox",
+                "spatie",
+                "v2"
+            ],
+            "time": "2017-11-18T21:56:03+00:00"
+        },
+        {
+            "name": "superbalist/flysystem-google-storage",
+            "version": "7.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Superbalist/flysystem-google-cloud-storage.git",
+                "reference": "97cf8a5c9a9d368260b2791ec130690a1bec0cbd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Superbalist/flysystem-google-cloud-storage/zipball/97cf8a5c9a9d368260b2791ec130690a1bec0cbd",
+                "reference": "97cf8a5c9a9d368260b2791ec130690a1bec0cbd",
+                "shasum": ""
+            },
+            "require": {
+                "google/cloud-storage": "~1.0",
+                "league/flysystem": "~1.0",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "0.9.*",
+                "phpunit/phpunit": "~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Superbalist\\Flysystem\\GoogleStorage\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Superbalist.com a division of Takealot Online (Pty) Ltd",
+                    "email": "info@superbalist.com"
+                }
+            ],
+            "description": "Flysystem adapter for Google Cloud Storage",
+            "time": "2019-02-21T08:07:24+00:00"
+        },
         {
         {
             "name": "symfony/polyfill-ctype",
             "name": "symfony/polyfill-ctype",
             "version": "v1.11.0",
             "version": "v1.11.0",

+ 108 - 14
install/index.php

@@ -4,6 +4,13 @@ require __DIR__ . '/../vendor/autoload.php';
 
 
 use App\Database\DB;
 use App\Database\DB;
 use App\Web\Session;
 use App\Web\Session;
+use Aws\S3\S3Client;
+use Google\Cloud\Storage\StorageClient;
+use League\Flysystem\Adapter\Local;
+use League\Flysystem\AwsS3v3\AwsS3Adapter;
+use League\Flysystem\Adapter\Ftp as FtpAdapter;
+use Spatie\Dropbox\Client as DropboxClient;
+use League\Flysystem\Filesystem;
 use Slim\App;
 use Slim\App;
 use Slim\Container;
 use Slim\Container;
 use Slim\Http\Environment;
 use Slim\Http\Environment;
@@ -11,21 +18,28 @@ use Slim\Http\Request;
 use Slim\Http\Response;
 use Slim\Http\Response;
 use Slim\Http\Uri;
 use Slim\Http\Uri;
 use Slim\Views\Twig;
 use Slim\Views\Twig;
+use Spatie\FlysystemDropbox\DropboxAdapter;
+use Superbalist\Flysystem\GoogleStorage\GoogleStorageAdapter;
 
 
 define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer.json'))->version);
 define('PLATFORM_VERSION', json_decode(file_get_contents(__DIR__ . '/../composer.json'))->version);
 
 
 $config = [
 $config = [
 	'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
 	'base_url' => isset($_SERVER['HTTPS']) ? 'https://' . $_SERVER['HTTP_HOST'] : 'http://' . $_SERVER['HTTP_HOST'],
-	'storage_dir' => 'storage',
-	'displayErrorDetails' => true,
 	'db' => [
 	'db' => [
 		'connection' => 'sqlite',
 		'connection' => 'sqlite',
 		'dsn' => 'resources/database/xbackbone.db',
 		'dsn' => 'resources/database/xbackbone.db',
 		'username' => null,
 		'username' => null,
 		'password' => null,
 		'password' => null,
 	],
 	],
+	'storage' => [
+		'driver' => 'local',
+	],
 ];
 ];
 
 
+if (file_exists(__DIR__ . '/../config.php')) {
+	$config = array_replace_recursive($config, require __DIR__ . '/../config.php');
+}
+
 $container = new Container(['settings' => $config]);
 $container = new Container(['settings' => $config]);
 
 
 $container['session'] = function ($container) {
 $container['session'] = function ($container) {
@@ -53,6 +67,47 @@ $container['view'] = function ($container) use (&$config) {
 	return $view;
 	return $view;
 };
 };
 
 
+$container['storage'] = function ($container) use (&$config) {
+
+	switch ($config['storage']['driver']) {
+		case 'local':
+			return new Filesystem(new Local($config['storage']['path']));
+		case 's3':
+			$client = new S3Client([
+				'credentials' => [
+					'key' => $config['storage']['key'],
+					'secret' => $config['storage']['secret'],
+				],
+				'region' => $config['storage']['region'],
+				'version' => 'latest',
+			]);
+
+			return new Filesystem(new AwsS3Adapter($client, $config['storage']['bucket'], $config['storage']['path']));
+		case 'dropbox':
+			$client = new DropboxClient($config['storage']['token']);
+			return new Filesystem(new DropboxAdapter($client), ['case_sensitive' => false]);
+		case 'ftp':
+			return new Filesystem(new FtpAdapter([
+				'host' => $config['storage']['host'],
+				'username' => $config['storage']['username'],
+				'password' => $config['storage']['password'],
+				'port' => $config['storage']['port'],
+				'root' => $config['storage']['path'],
+				'passive' => $config['storage']['passive'],
+				'ssl' => $config['storage']['ssl'],
+				'timeout' => 30,
+			]));
+		case 'google-cloud':
+			$client = new StorageClient([
+				'projectId' => $config['storage']['project_id'],
+				'keyFilePath' => $config['storage']['key_path'],
+			]);
+			return new Filesystem(new GoogleStorageAdapter($client, $client->bucket($config['storage']['bucket'])));
+		default:
+			throw new InvalidArgumentException('The driver specified is not supported.');
+	}
+};
+
 function migrate($config)
 function migrate($config)
 {
 {
 	$firstMigrate = false;
 	$firstMigrate = false;
@@ -137,28 +192,64 @@ $app->get('/', function (Request $request, Response $response) {
 });
 });
 
 
 $app->post('/', function (Request $request, Response $response) use (&$config) {
 $app->post('/', function (Request $request, Response $response) use (&$config) {
+
+	// Check if there is a previous installation, if not, setup the config file
 	$installed = true;
 	$installed = true;
 	if (!file_exists(__DIR__ . '/../config.php')) {
 	if (!file_exists(__DIR__ . '/../config.php')) {
 		$installed = false;
 		$installed = false;
 
 
+		// config file setup
 		$config['base_url'] = $request->getParam('base_url');
 		$config['base_url'] = $request->getParam('base_url');
-		$config['storage_dir'] = $request->getParam('storage_dir');
+		$config['storage']['driver'] = $request->getParam('storage_driver');
 		unset($config['displayErrorDetails']);
 		unset($config['displayErrorDetails']);
 		$config['db']['connection'] = $request->getParam('connection');
 		$config['db']['connection'] = $request->getParam('connection');
 		$config['db']['dsn'] = $request->getParam('dsn');
 		$config['db']['dsn'] = $request->getParam('dsn');
 		$config['db']['username'] = $request->getParam('db_user');
 		$config['db']['username'] = $request->getParam('db_user');
 		$config['db']['password'] = $request->getParam('db_password');
 		$config['db']['password'] = $request->getParam('db_password');
 
 
+
+		// setup storage configuration
+		switch ($config['storage']['driver']) {
+			case 's3':
+				$config['storage']['key'] = $request->getParam('storage_key');
+				$config['storage']['secret'] = $request->getParam('storage_secret');
+				$config['storage']['region'] = $request->getParam('storage_region');
+				$config['storage']['bucket'] = $request->getParam('storage_bucket');
+				$config['storage']['path'] = $request->getParam('storage_path');
+				break;
+			case 'dropbox':
+				$config['storage']['token'] = $request->getParam('storage_token');
+				break;
+			case 'ftp':
+				$config['storage']['host'] = $request->getParam('storage_host');
+				$config['storage']['username'] = $request->getParam('storage_username');
+				$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');
+				break;
+			case 'google-cloud':
+				$config['storage']['project_id'] = $request->getParam('storage_project_id');
+				$config['storage']['key_path'] = $request->getParam('storage_key_path');
+				$config['storage']['bucket'] = $request->getParam('storage_bucket');
+				break;
+			case 'local':
+			default:
+				$config['storage']['path'] = $request->getParam('storage_path');
+				break;
+		}
+
+		// check if the storage is valid
 		try {
 		try {
-			storage($config['storage_dir']);
-		} catch (LogicException $exception) {
-			$this->session->alert('The storage folder is not readable (' . $config['storage_dir'] . ')', 'danger');
-			return redirect($response, './');
-		} finally {
-			if (!is_writable($config['storage_dir'])) {
-				$this->session->alert('The storage folder is not writable (' . $config['storage_dir'] . ')', 'danger');
-				return redirect($response, './');
+			$success = $this->storage->write('test.install.txt', '');
+			$this->storage->readAndDelete('test.install.txt');
+			if (!$success) {
+				throw new Exception('The storage is not writable.');
 			}
 			}
+		} catch (Exception $e) {
+			$this->session->alert("Storage setup error: {$e->getMessage()} [{$e->getTraceAsString()}]", 'danger');
+			return redirect($response, './');
 		}
 		}
 
 
 		$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
 		$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
@@ -166,13 +257,13 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
 			$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
 			$this->session->alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
 			return redirect($response, './');
 			return redirect($response, './');
 		}
 		}
-	} else {
-		$config = require __DIR__ . '/../config.php';
 	}
 	}
 
 
-	$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn'];
 
 
+	// Build the dns string and run the migrations
 	try {
 	try {
+
+		$dsn = $config['db']['connection'] === 'sqlite' ? __DIR__ . '/../' . $config['db']['dsn'] : $config['db']['dsn'];
 		DB::setDsn($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
 		DB::setDsn($config['db']['connection'] . ':' . $dsn, $config['db']['username'], $config['db']['password']);
 
 
 		migrate($config);
 		migrate($config);
@@ -181,15 +272,18 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
 		return redirect($response, './');
 		return redirect($response, './');
 	}
 	}
 
 
+	// if not installed, create the default admin account
 	if (!$installed) {
 	if (!$installed) {
 		DB::doQuery("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)]);
 		DB::doQuery("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)]);
 	}
 	}
 
 
+	// post install cleanup
 	cleanDirectory(__DIR__ . '/../resources/cache');
 	cleanDirectory(__DIR__ . '/../resources/cache');
 	cleanDirectory(__DIR__ . '/../resources/sessions');
 	cleanDirectory(__DIR__ . '/../resources/sessions');
 
 
 	removeDirectory(__DIR__ . '/../install');
 	removeDirectory(__DIR__ . '/../install');
 
 
+	// if is upgrading and existing installation, put it out maintenance
 	if ($installed) {
 	if ($installed) {
 		unset($config['maintenance']);
 		unset($config['maintenance']);