Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sergix44 committed Apr 28, 2018
1 parent 3f1f13a commit 805d1d0
Show file tree
Hide file tree
Showing 168 changed files with 42,650 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.idea/
vendor/
storage/
!resources/cache/.gitkeep
resources/database/*.db
resources/sessions/sess_*
logs/log-*.txt
config.php
6 changes: 6 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Options -Indexes
RewriteEngine On
RewriteRule ^(app|bin|bootstrap|resources|storage|vendor|logs)(/.*|)$ - [NC,F]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## v1.1
+ Added logging.
+ Fixed back to top when click delete or publish/unpublish.
+ Improved migrate system.
+ Login redirect back to the requested page.
+ Updated Bootstrap theme.
+ Added share to Telegram.

## v1.0
+ Initial version.
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# XBackBone 📤
XBackBone is a simple, self-hosted, lightweight PHP backend for the instant sharing tool ShareX. It supports uploading and displaying images, GIF, video, code, formatted text, and file downloading and uploading. Also have a web UI with multi user managemant and past uploads history.

## Features

+ Supports every upload type from ShareX.
+ User management, multi user features.
+ Public and private uploads.
+ Web UI for each user.
+ Logging system.

## How to Install
XBackBone require PHP >= `7.1`, the composer package manager and writable storage path:

+ Clone this repository in your web root folder:

```sh
git clone http://github.com/SergiX44/XBackBone .
```
+ Run a composer from your shell:

```sh
composer install --no-dev
```
+ Setup the config file:

```sh
cp config.example.php config.php
```
By default, XBackBone will use Sqlite as DB engine, and a `storage` dir in the current directory. You can leave these settings unchanged for a simple personal installation.
You must set the `base_url`, or remove it for get dynamically the url from request (not raccomanded).

```php
return [
'base_url' => 'https://myaswesomedomain.com', // no trailing slash
'storage_dir' => 'storage',
'db' => [
'connection' => 'sqlite',
'dsn' => 'resources/database/xbackbone.db',
'username' => null, // username and password not needed for sqlite
'password' => null,
]
];
```
+ Finally, run the migrate script to setup the database

```sh
php bin/migrate --install
```
+ Now just login with `admin/admin`, **be sure to change these credentials after your first login**.

## Notes
If you do not use Apache, or the Apache `.htaccess` is not enabled, set your web server so that the `static/` folder is the only one accessible from the outside, otherwise even private uploads and logs will be accessible!
60 changes: 60 additions & 0 deletions app/Controllers/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace App\Controllers;


use App\Exceptions\AuthenticationException;
use App\Exceptions\UnauthorizedException;
use App\Web\Session;
use Flight;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;

abstract class Controller
{

/**
* @throws AuthenticationException
*/
protected function checkLogin(): void
{
if (!Session::get('logged', false)) {
Session::set('redirectTo', (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
throw new AuthenticationException();
}
}

/**
* @throws AuthenticationException
* @throws UnauthorizedException
*/
protected function checkAdmin(): void
{
$this->checkLogin();
if (!Session::get('admin', false)) {
throw new UnauthorizedException();
}
}


protected function humanFilesize($size, $precision = 2): string
{
for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {
}
return round($size, $precision) . ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i];
}

protected function getStorage(): Filesystem
{
return new Filesystem(new Local(Flight::get('config')['storage_dir']));
}

protected function http2push(string $url, string $as = 'image'): void
{
if (Flight::request()->scheme === 'HTTP/2.0') {
$headers = isset(Flight::response()->headers()['Link']) ? Flight::response()->headers()['Link'] : [];
$headers[] = "<${url}>; rel=preload; as=${as}";
Flight::response()->header('Link', $headers);
}
}
}
93 changes: 93 additions & 0 deletions app/Controllers/DashboardController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace App\Controllers;


use App\Database\DB;
use App\Traits\SingletonController;
use App\Web\Session;
use Flight;
use League\Flysystem\FileNotFoundException;

class DashboardController extends Controller
{
use SingletonController;

const PER_PAGE = 21;
const PER_PAGE_ADMIN = 50;

public function redirects(): void
{
$this->checkLogin();
Flight::redirect('/home');
}

public function home($page = 1): void
{
$this->checkLogin();

$page = max(0, --$page);

if (Session::get('admin', false)) {
$medias = DB::query('SELECT `uploads`.*, `users`.`user_code`, `users`.`username` FROM `uploads` LEFT JOIN `users` ON `uploads`.`user_id` = `users`.`id` ORDER BY `timestamp` DESC LIMIT ? OFFSET ?', [self::PER_PAGE_ADMIN, $page * self::PER_PAGE_ADMIN])->fetchAll();
$pages = DB::query('SELECT COUNT(*) AS `count` FROM `uploads`')->fetch()->count / self::PER_PAGE_ADMIN;
} else {
$medias = DB::query('SELECT `uploads`.*,`users`.`user_code`, `users`.`username` FROM `uploads` INNER JOIN `users` ON `uploads`.`user_id` = `users`.`id` WHERE `user_id` = ? ORDER BY `timestamp` DESC LIMIT ? OFFSET ?', [Session::get('user_id'), self::PER_PAGE, $page * self::PER_PAGE])->fetchAll();
$pages = DB::query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` = ?', Session::get('user_id'))->fetch()->count / self::PER_PAGE;
}

$filesystem = $this->getStorage();
$base_url = Flight::get('config')['base_url'];

foreach ($medias as $media) {
$extension = pathinfo($media->filename, PATHINFO_EXTENSION);
try {
$mime = $filesystem->getMimetype($media->storage_path);
} catch (FileNotFoundException $e) {
$mime = null;
}
$media->mimetype = $mime;
$media->extension = $extension;

if ($type = explode('/', $mime)[0] === 'image') {
$this->http2push("$base_url/$media->user_code/$media->code.$extension/raw");
}
}

Flight::render(
Session::get('admin', false) ? 'dashboard/admin.twig' : 'dashboard/home.twig',
[
'medias' => $medias,
'next' => $page < floor($pages),
'previous' => $page >= 1,
'current_page' => ++$page,
]
);
}

public function system()
{
$this->checkAdmin();

$usersCount = DB::query('SELECT COUNT(*) AS `count` FROM `users`')->fetch()->count;
$mediasCount = DB::query('SELECT COUNT(*) AS `count` FROM `uploads`')->fetch()->count;
$orphanFilesCount = DB::query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` IS NULL')->fetch()->count;

$medias = DB::query('SELECT `users`.`user_code`, `uploads`.`code`, `uploads`.`storage_path` FROM `uploads` LEFT JOIN `users` ON `uploads`.`user_id` = `users`.`id`')->fetchAll();

$totalSize = 0;

$filesystem = $this->getStorage();
foreach ($medias as $media) {
$totalSize += $filesystem->getSize($media->storage_path);
}

Flight::render('dashboard/system.twig', [
'usersCount' => $usersCount,
'mediasCount' => $mediasCount,
'orphanFilesCount' => $orphanFilesCount,
'totalSize' => $this->humanFilesize($totalSize),
'max_filesize' => ini_get('post_max_size') . '/' . ini_get('upload_max_filesize'),
]);
}
}
68 changes: 68 additions & 0 deletions app/Controllers/LoginController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace App\Controllers;


use App\Database\DB;
use App\Traits\SingletonController;
use App\Web\Log;
use App\Web\Session;
use Flight;

class LoginController extends Controller
{
use SingletonController;

public function show(): void
{
if (Session::get('logged', false)) {
Flight::redirect('/home');
return;
}
Flight::render('auth/login.twig');
}

public function login(): void
{
$form = Flight::request()->data;

$result = DB::query('SELECT `id`,`username`, `password`,`is_admin`, `active` FROM `users` WHERE `username` = ? LIMIT 1', $form->username)->fetch();

if (!$result || !password_verify($form->password, $result->password)) {
Flight::redirect('login');
Session::alert('Wrong credentials', 'danger');
return;
}

if (!$result->active) {
Flight::redirect('login');
Session::alert('Your account is disabled.', 'danger');
return;
}

Session::set('logged', true);
Session::set('user_id', $result->id);
Session::set('username', $result->username);
Session::set('admin', $result->is_admin);

Session::alert("Welcome, $result->username!", 'info');
Log::info("User $result->username logged in.");

if (Session::has('redirectTo')) {
Flight::redirect(Session::get('redirectTo'));
return;
}

Flight::redirect('/home');
}

public function logout(): void
{
$this->checkLogin();
Session::clear();
Session::set('logged', false);
Session::alert('Goodbye!', 'warning');
Flight::redirect('/login');
}

}
Loading

0 comments on commit 805d1d0

Please sign in to comment.