mirror of
https://github.com/devfake/flox.git
synced 2024-11-08 19:32:29 +01:00
Init new version
This commit is contained in:
parent
a7cba7dc2e
commit
b754d84149
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.scss linguist-vendored
|
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
.idea
|
||||
.env
|
||||
|
||||
/backend/vendor
|
||||
/client/node_modules
|
||||
/backend/vendor
|
||||
/.idea
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
/backend/.env
|
||||
|
||||
*/.DS_Store
|
||||
/public/assets/poster/*.jpg
|
61
README.md
61
README.md
@ -1 +1,60 @@
|
||||
New version is coming soon
|
||||
![flox](http://80.240.132.120/flox-demo/public/assets/img/dark.jpg)
|
||||
|
||||
Flox
|
||||
===============
|
||||
Flox is a self hosted Movie watch list. It's build on top of PHP (Laravel), MySQL and Vue.js and uses [The Movie Database](https://www.themoviedb.org/) API.
|
||||
The rating based on an 3-Point system for `good`, `medium` and `bad`.
|
||||
|
||||
[Try live demo](http://80.240.132.120/flox-demo/public/) and [login](http://80.240.132.120/flox-demo/public/login) with `demo / demo` to add new movies or change ratings.
|
||||
|
||||
### Requirements
|
||||
|
||||
* PHP >=5.6.4
|
||||
* [Composer](https://getcomposer.org/)
|
||||
* [The Movie Database](https://www.themoviedb.org/) Account for the [API-Key](https://www.themoviedb.org/faq/api)
|
||||
|
||||
### Install
|
||||
|
||||
* Download Flox and `cd` into `backend` and run
|
||||
```bash
|
||||
composer install
|
||||
php artisan flox:init # Enter here your database credentials
|
||||
php artisan flox:db # Running migrations and enter your admin credentials for the site
|
||||
```
|
||||
* Enter your TMDB API-Key in `backend/.env`
|
||||
* Set your `CLIENT_URI` in `backend/.env`. If you use something like XAMPP or MAMP, the CLIENT_URI should be `/flox/public`. If you use something like Homestead, the CLIENT_URI should be `/`.
|
||||
|
||||
### Alternative Language
|
||||
|
||||
All movie titles are in english by default. You can check if TMDB has an alternative title by setting `ALTERNATIVE_LANGUAGE` in `backend/.env`.
|
||||
The most commons are `DE`, `IT`, `FR`, `ES` and `RU`.
|
||||
|
||||
### Better Search
|
||||
|
||||
You can use [Laravel Scout](https://laravel.com/docs/master/scout) for better search results with typos. Something like `findg nemo`.
|
||||
Add your driver and API-Keys in `backend/.env` and uncomment `use Searchable;` in `backend/app/Item.php` on line 11.
|
||||
|
||||
To sync your own movie list with your Laravel Scout driver, run `php artisan flox:sync` (If you using Laravel Scout later).
|
||||
|
||||
[Algolia](https://www.algolia.com/) is a great service and has a free hacker plan.
|
||||
|
||||
Note: Laravel Scout is on the demo site not active.
|
||||
|
||||
### Development
|
||||
|
||||
* Run `npm install` in your `/client` folder.
|
||||
* Make sure you have installed `webpack` globally.
|
||||
* Run `npm run dev` or `npm run build`.
|
||||
|
||||
### Misc
|
||||
|
||||
* Give `backend/storage` and `public/assets` recursive write access.
|
||||
|
||||
### Todo
|
||||
|
||||
* Settings
|
||||
* Change username / password
|
||||
* Export and import
|
||||
* Sync scout driver
|
||||
* Ajax request for settings
|
||||
* Better responsive
|
||||
|
18
backend/.env.example
Normal file
18
backend/.env.example
Normal file
@ -0,0 +1,18 @@
|
||||
TMDB_API_KEY=
|
||||
ALTERNATIVE_LANGUAGE=
|
||||
|
||||
CLIENT_URI=/
|
||||
LOADING_ITEMS=30
|
||||
|
||||
SCOUT_DRIVER=
|
||||
ALGOLIA_APP_ID=
|
||||
ALGOLIA_SECRET=
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_DATABASE=
|
||||
DB_USERNAME=
|
||||
DB_PASSWORD=
|
||||
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
49
backend/app/Console/Commands/DB.php
Normal file
49
backend/app/Console/Commands/DB.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DB extends Command {
|
||||
|
||||
protected $signature = 'flox:db';
|
||||
protected $description = 'Create database migrations and admin account';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$this->createMigrations();
|
||||
} catch(\Exception $e) {
|
||||
$this->error('Can not connect to the database. Error: ' . $e->getMessage());
|
||||
$this->error('Make sure your database credentials in .env are correct');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->createUser();
|
||||
}
|
||||
|
||||
private function createMigrations()
|
||||
{
|
||||
$this->info('TRYING TO MIGRATE DATABASE');
|
||||
$this->call('migrate', ['--force' => true]);
|
||||
$this->info('MIGRATION COMPLETED');
|
||||
}
|
||||
|
||||
private function createUser()
|
||||
{
|
||||
$username = $this->ask('Enter your admin username');
|
||||
$password = $this->ask('Enter your admin password');
|
||||
|
||||
$user = new User();
|
||||
$user->username = $username;
|
||||
$user->password = bcrypt($password);
|
||||
$user->save();
|
||||
}
|
||||
}
|
64
backend/app/Console/Commands/Init.php
Normal file
64
backend/app/Console/Commands/Init.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Init extends Command {
|
||||
|
||||
protected $signature = 'flox:init';
|
||||
protected $description = 'Create .env file, set the app key and fill database credentials';
|
||||
|
||||
private $requests = [
|
||||
'DB_DATABASE' => 'Name',
|
||||
'DB_USERNAME' => 'Username',
|
||||
'DB_PASSWORD' => 'Password',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->createENVFile();
|
||||
$this->fillDatabaseCredentials();
|
||||
$this->setAppKey();
|
||||
}
|
||||
|
||||
private function createENVFile()
|
||||
{
|
||||
if( ! file_exists('.env')) {
|
||||
$this->info('CREATING .ENV FILE');
|
||||
copy('.env.example', '.env');
|
||||
}
|
||||
}
|
||||
|
||||
private function fillDatabaseCredentials()
|
||||
{
|
||||
foreach($this->requests as $type => $text) {
|
||||
if( ! env($type)) {
|
||||
$value = $this->ask('Enter your Database ' . $text);
|
||||
$this->changeENV($type, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setAppKey()
|
||||
{
|
||||
if( ! env('APP_KEY')) {
|
||||
$this->info('GENERATING APP KEY');
|
||||
$this->callSilent('key:generate');
|
||||
}
|
||||
}
|
||||
|
||||
private function changeENV($type, $value)
|
||||
{
|
||||
file_put_contents('.env', str_replace(
|
||||
$type . '=',
|
||||
$type . '=' . $value,
|
||||
file_get_contents('.env')
|
||||
));
|
||||
}
|
||||
}
|
32
backend/app/Console/Commands/Sync.php
Normal file
32
backend/app/Console/Commands/Sync.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Sync extends Command {
|
||||
|
||||
protected $signature = 'flox:sync';
|
||||
protected $description = 'Synchronize your movies with your scout search driver';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$scoutDriver = env('SCOUT_DRIVER');
|
||||
|
||||
try {
|
||||
$this->info('TRYING TO SYNC YOUR MOVIES TO ' . strtoupper($scoutDriver));
|
||||
$this->call('scout:import', ['model' => 'App\\Item']);
|
||||
$this->info('SYNCHRONIZATION COMPLETED');
|
||||
} catch(\Exception $e) {
|
||||
$this->error('Can not connect to ' . $scoutDriver . '. Error: ' . $e->getMessage());
|
||||
$this->error('Make sure your ' . $scoutDriver . ' credentials in .env are correct');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
42
backend/app/Console/Kernel.php
Normal file
42
backend/app/Console/Kernel.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
Commands\Init::class,
|
||||
Commands\DB::class,
|
||||
Commands\Sync::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// $schedule->command('inspire')
|
||||
// ->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Closure based commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
65
backend/app/Exceptions/Handler.php
Normal file
65
backend/app/Exceptions/Handler.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that should not be reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
\Illuminate\Auth\AuthenticationException::class,
|
||||
\Illuminate\Auth\Access\AuthorizationException::class,
|
||||
\Symfony\Component\HttpKernel\Exception\HttpException::class,
|
||||
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
|
||||
\Illuminate\Session\TokenMismatchException::class,
|
||||
\Illuminate\Validation\ValidationException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $exception
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $exception)
|
||||
{
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an authentication exception into an unauthenticated response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Auth\AuthenticationException $exception
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['error' => 'Unauthenticated.'], 401);
|
||||
}
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
}
|
13
backend/app/Http/Controllers/Controller.php
Normal file
13
backend/app/Http/Controllers/Controller.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
}
|
152
backend/app/Http/Controllers/ItemController.php
Normal file
152
backend/app/Http/Controllers/ItemController.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Item;
|
||||
use App\Services\TMDB;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ItemController {
|
||||
|
||||
private $loadingItems;
|
||||
private $item;
|
||||
|
||||
/**
|
||||
* Get the amout of loading items and create an instance for 'item'.
|
||||
*
|
||||
* @param Item $item
|
||||
*/
|
||||
public function __construct(Item $item)
|
||||
{
|
||||
$this->loadingItems = config('app.LOADING_ITEMS');
|
||||
$this->item = $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all items for home with pagination.
|
||||
*
|
||||
* @param $orderBy
|
||||
* @return mixed
|
||||
*/
|
||||
public function items($orderBy)
|
||||
{
|
||||
$orderType = $orderBy == 'rating' ? 'asc' : 'desc';
|
||||
|
||||
return $this->item->orderBy($orderBy, $orderType)->simplePaginate($this->loadingItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for items by 'title' in database or with Laravel Scout.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$title = Input::get('q');
|
||||
|
||||
if(config('scout.driver')) {
|
||||
return $this->item->search($title)->get();
|
||||
}
|
||||
|
||||
// We don't have an smart search driver and return an simple 'like' query.
|
||||
return $this->item->where('title', 'LIKE', '%' . $title . '%')
|
||||
->orWhere('alternative_title', 'LIKE', '%' . $title . '%')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rating for an movie.
|
||||
*
|
||||
* @param $itemID
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function changeRating($itemID)
|
||||
{
|
||||
$item = $this->item->find($itemID);
|
||||
|
||||
if( ! $item) {
|
||||
return response('Not Found', 404);
|
||||
}
|
||||
|
||||
$item->update([
|
||||
'rating' => Input::get('rating')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new movie to database and create the poster image file.
|
||||
*
|
||||
* @param TMDB $tmdb
|
||||
* @return Item
|
||||
*/
|
||||
public function add(TMDB $tmdb)
|
||||
{
|
||||
$data = Input::get('item');
|
||||
|
||||
$this->createPosterFile($data['poster']);
|
||||
|
||||
return $this->createItem($data, $tmdb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete movie in database and delete the poster image file.
|
||||
*
|
||||
* @param $itemID
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function remove($itemID)
|
||||
{
|
||||
$item = $this->item->find($itemID);
|
||||
|
||||
if( ! $item) {
|
||||
return response('Not Found', 404);
|
||||
}
|
||||
|
||||
$this->removePosterFile($item->poster);
|
||||
|
||||
$item->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the new movie.
|
||||
*
|
||||
* @param $data
|
||||
* @param $tmdb
|
||||
* @return Item
|
||||
*/
|
||||
private function createItem($data, $tmdb)
|
||||
{
|
||||
return $this->item->create([
|
||||
'tmdb_id' => $data['tmdb_id'],
|
||||
'title' => $data['title'],
|
||||
'alternative_title' => $tmdb->alternativeMovieTitle($data["tmdb_id"]),
|
||||
'poster' => $data['poster'],
|
||||
'rating' => 1,
|
||||
'released' => $data['released'],
|
||||
'created_at' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the poster image file.
|
||||
*
|
||||
* @param $poster
|
||||
*/
|
||||
private function createPosterFile($poster)
|
||||
{
|
||||
if($poster) {
|
||||
Storage::put($poster, file_get_contents('http://image.tmdb.org/t/p/w185' . $poster));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the poster image file.
|
||||
*
|
||||
* @param $poster
|
||||
*/
|
||||
private function removePosterFile($poster)
|
||||
{
|
||||
Storage::delete($poster);
|
||||
}
|
||||
}
|
21
backend/app/Http/Controllers/TMDBController.php
Normal file
21
backend/app/Http/Controllers/TMDBController.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\TMDB;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
|
||||
class TMDBController {
|
||||
|
||||
private $tmdb;
|
||||
|
||||
public function __construct(TMDB $tmdb)
|
||||
{
|
||||
$this->tmdb = $tmdb;
|
||||
}
|
||||
|
||||
public function search()
|
||||
{
|
||||
return $this->tmdb->search(Input::get('q'));
|
||||
}
|
||||
}
|
50
backend/app/Http/Controllers/UserController.php
Normal file
50
backend/app/Http/Controllers/UserController.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
|
||||
class UserController {
|
||||
|
||||
private $auth;
|
||||
|
||||
/**
|
||||
* Create an instance for 'auth'.
|
||||
*
|
||||
* @param Guard $auth
|
||||
*/
|
||||
public function __construct(Guard $auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login user and return correct response.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
$username = Input::get('username');
|
||||
$password = Input::get('password');
|
||||
|
||||
if($this->auth->attempt(['username' => $username, 'password' => $password], true)) {
|
||||
return response('Success', 200);
|
||||
}
|
||||
|
||||
return response('Unauthorized', 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout user and redirect to home.
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
$this->auth->logout();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
56
backend/app/Http/Kernel.php
Normal file
56
backend/app/Http/Kernel.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:60,1',
|
||||
'bindings',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
];
|
||||
}
|
17
backend/app/Http/Middleware/EncryptCookies.php
Normal file
17
backend/app/Http/Middleware/EncryptCookies.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
|
||||
|
||||
class EncryptCookies extends BaseEncrypter
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
26
backend/app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
26
backend/app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $guard
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
{
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect('/home');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
17
backend/app/Http/Middleware/VerifyCsrfToken.php
Normal file
17
backend/app/Http/Middleware/VerifyCsrfToken.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
|
||||
|
||||
class VerifyCsrfToken extends BaseVerifier
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
24
backend/app/Item.php
Normal file
24
backend/app/Item.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Laravel\Scout\Searchable;
|
||||
|
||||
class Item extends Model {
|
||||
|
||||
// Uncomment this if you are using Laravel Scout.
|
||||
//use Searchable;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'tmdb_id',
|
||||
'title',
|
||||
'alternative_title',
|
||||
'poster',
|
||||
'rating',
|
||||
'released',
|
||||
'created_at',
|
||||
];
|
||||
}
|
28
backend/app/Providers/AppServiceProvider.php
Normal file
28
backend/app/Providers/AppServiceProvider.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
30
backend/app/Providers/AuthServiceProvider.php
Normal file
30
backend/app/Providers/AuthServiceProvider.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
'App\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
//
|
||||
}
|
||||
}
|
26
backend/app/Providers/BroadcastServiceProvider.php
Normal file
26
backend/app/Providers/BroadcastServiceProvider.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
/*
|
||||
* Authenticate the user's personal channel...
|
||||
*/
|
||||
Broadcast::channel('App.User.*', function ($user, $userId) {
|
||||
return (int) $user->id === (int) $userId;
|
||||
});
|
||||
}
|
||||
}
|
32
backend/app/Providers/EventServiceProvider.php
Normal file
32
backend/app/Providers/EventServiceProvider.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
'App\Events\SomeEvent' => [
|
||||
'App\Listeners\EventListener',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
//
|
||||
}
|
||||
}
|
79
backend/app/Providers/RouteServiceProvider.php
Normal file
79
backend/app/Providers/RouteServiceProvider.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* This namespace is applied to your controller routes.
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'App\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::group([
|
||||
'middleware' => 'web',
|
||||
'namespace' => $this->namespace,
|
||||
], function ($router) {
|
||||
require base_path('routes/web.php');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapApiRoutes()
|
||||
{
|
||||
Route::group([
|
||||
'middleware' => 'api',
|
||||
'namespace' => $this->namespace,
|
||||
'prefix' => 'api',
|
||||
], function ($router) {
|
||||
require base_path('routes/api.php');
|
||||
});
|
||||
}
|
||||
}
|
70
backend/app/Services/TMDB.php
Normal file
70
backend/app/Services/TMDB.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use DateTime;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class TMDB {
|
||||
|
||||
private $client;
|
||||
private $apiKey;
|
||||
|
||||
/**
|
||||
* Get the API Key for TMDB and create an instance of Guzzle.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->apiKey = config('app.TMDB_API_KEY');
|
||||
$this->client = new Client(['base_uri' => 'http://api.themoviedb.org/']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search TMDB for an movie by 'title'.
|
||||
*
|
||||
* @param $title
|
||||
* @return array
|
||||
*/
|
||||
public function search($title)
|
||||
{
|
||||
$items = [];
|
||||
|
||||
$response = $this->client->get('/3/search/movie', ['query' => ['api_key' => $this->apiKey, 'query' => $title]]);
|
||||
$response = json_decode($response->getBody());
|
||||
|
||||
foreach($response->results as $result) {
|
||||
$dtime = DateTime::createFromFormat('Y-m-d', ($result->release_date ?: '1970-12-1'));
|
||||
$items[] = [
|
||||
'tmdb_id' => $result->id,
|
||||
'title' => $result->title,
|
||||
'poster' => $result->poster_path,
|
||||
'released' => $dtime->getTimestamp(),
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new request to TMDB to get the 'alternative language' movie title.
|
||||
*
|
||||
* @param $tmdb_id
|
||||
* @return null|string
|
||||
*/
|
||||
public function alternativeMovieTitle($tmdb_id)
|
||||
{
|
||||
$alternativeLanguage = config('app.ALTERNATIVE_LANGUAGE');
|
||||
|
||||
$response = $this->client->get('/3/movie/' . $tmdb_id . '/alternative_titles', ['query' => ['api_key' => $this->apiKey]]);
|
||||
$titles = collect(json_decode($response->getBody())->titles);
|
||||
|
||||
$title = $titles->where('iso_3166_1', $alternativeLanguage);
|
||||
|
||||
if($title->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// '3D' is often used in the title. We don't need them.
|
||||
return trim(str_replace('3D', '', $title->first()->title));
|
||||
}
|
||||
}
|
16
backend/app/User.php
Normal file
16
backend/app/User.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
||||
class User extends Authenticatable {
|
||||
|
||||
protected $fillable = [
|
||||
'username', 'password',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password', 'remember_token',
|
||||
];
|
||||
}
|
51
backend/artisan
Normal file
51
backend/artisan
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/bootstrap/autoload.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Artisan Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When we run the console application, the current CLI command will be
|
||||
| executed in this console and the response sent back to a terminal
|
||||
| or another output device for the developers. Here goes nothing!
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
|
||||
$status = $kernel->handle(
|
||||
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||
new Symfony\Component\Console\Output\ConsoleOutput
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running. We will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
|
||||
exit($status);
|
55
backend/bootstrap/app.php
Normal file
55
backend/bootstrap/app.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Create The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The first thing we will do is create a new Laravel application instance
|
||||
| which serves as the "glue" for all the components of Laravel, and is
|
||||
| the IoC container for the system binding all of the various parts.
|
||||
|
|
||||
*/
|
||||
|
||||
$app = new Illuminate\Foundation\Application(
|
||||
realpath(__DIR__.'/../')
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bind Important Interfaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, we need to bind some important interfaces into the container so
|
||||
| we will be able to resolve them when needed. The kernels serve the
|
||||
| incoming requests to this application from both the web and CLI.
|
||||
|
|
||||
*/
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Http\Kernel::class,
|
||||
App\Http\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Console\Kernel::class,
|
||||
App\Console\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
App\Exceptions\Handler::class
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Return The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This script returns the application instance. The instance is given to
|
||||
| the calling script so we can separate the building of the instances
|
||||
| from the actual running of the application and sending responses.
|
||||
|
|
||||
*/
|
||||
|
||||
return $app;
|
34
backend/bootstrap/autoload.php
Normal file
34
backend/bootstrap/autoload.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Composer Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include The Compiled Class File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| To dramatically increase your application's performance, you may use a
|
||||
| compiled class file which contains all of the classes commonly used
|
||||
| by a request. The Artisan "optimize" is used to create this file.
|
||||
|
|
||||
*/
|
||||
|
||||
$compiledPath = __DIR__.'/cache/compiled.php';
|
||||
|
||||
if (file_exists($compiledPath)) {
|
||||
require $compiledPath;
|
||||
}
|
2
backend/bootstrap/cache/.gitignore
vendored
Normal file
2
backend/bootstrap/cache/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
53
backend/composer.json
Normal file
53
backend/composer.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "laravel/laravel",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": ["framework", "laravel"],
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=5.6.4",
|
||||
"laravel/framework": "5.3.*",
|
||||
"laravel/scout": "^1.1",
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"algolia/algoliasearch-client-php": "^1.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "~1.4",
|
||||
"mockery/mockery": "0.9.*",
|
||||
"phpunit/phpunit": "~5.0",
|
||||
"symfony/css-selector": "3.1.*",
|
||||
"symfony/dom-crawler": "3.1.*"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"database"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-root-package-install": [
|
||||
"php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"php artisan key:generate"
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postInstall",
|
||||
"php artisan optimize"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
|
||||
"php artisan optimize"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
}
|
||||
}
|
3786
backend/composer.lock
generated
Normal file
3786
backend/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
238
backend/config/app.php
Normal file
238
backend/config/app.php
Normal file
@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'TMDB_API_KEY' => env('TMDB_API_KEY'),
|
||||
'ALTERNATIVE_LANGUAGE' => env('ALTERNATIVE_LANGUAGE'),
|
||||
'LOADING_ITEMS' => env('LOADING_ITEMS'),
|
||||
'CLIENT_URI' => env('CLIENT_URI'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application. This value is used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| any other location as required by the application or its packages.
|
||||
*/
|
||||
|
||||
'name' => 'Flox',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services your application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| your application so that it is used when running Artisan tasks.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. We have gone
|
||||
| ahead and set this to a sensible default for you out of the box.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by the translation service provider. You are free to set this value
|
||||
| to any of the locales which will be supported by the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => 'en',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Fallback Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The fallback locale determines the locale to use when the current one
|
||||
| is not available. You may change the value to correspond to any of
|
||||
| the language folders that are provided through your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback_locale' => 'en',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is used by the Illuminate encrypter service and should be set
|
||||
| to a random, 32 character string, otherwise these encrypted strings
|
||||
| will not be safe. Please do this before deploying an application!
|
||||
|
|
||||
*/
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Logging Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log settings for your application. Out of
|
||||
| the box, Laravel uses the Monolog PHP logging library. This gives
|
||||
| you a variety of powerful log handlers / formatters to utilize.
|
||||
|
|
||||
| Available Settings: "single", "daily", "syslog", "errorlog"
|
||||
|
|
||||
*/
|
||||
|
||||
'log' => env('APP_LOG', 'daily'),
|
||||
|
||||
'log_level' => env('APP_LOG_LEVEL', 'debug'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Autoloaded Service Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The service providers listed here will be automatically loaded on the
|
||||
| request to your application. Feel free to add your own services to
|
||||
| this array to grant expanded functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
|
||||
/*
|
||||
* Laravel Framework Service Providers...
|
||||
*/
|
||||
Illuminate\Auth\AuthServiceProvider::class,
|
||||
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||
Illuminate\Bus\BusServiceProvider::class,
|
||||
Illuminate\Cache\CacheServiceProvider::class,
|
||||
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
|
||||
Illuminate\Cookie\CookieServiceProvider::class,
|
||||
Illuminate\Database\DatabaseServiceProvider::class,
|
||||
Illuminate\Encryption\EncryptionServiceProvider::class,
|
||||
Illuminate\Filesystem\FilesystemServiceProvider::class,
|
||||
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
|
||||
Illuminate\Hashing\HashServiceProvider::class,
|
||||
Illuminate\Mail\MailServiceProvider::class,
|
||||
Illuminate\Notifications\NotificationServiceProvider::class,
|
||||
Illuminate\Pagination\PaginationServiceProvider::class,
|
||||
Illuminate\Pipeline\PipelineServiceProvider::class,
|
||||
Illuminate\Queue\QueueServiceProvider::class,
|
||||
Illuminate\Redis\RedisServiceProvider::class,
|
||||
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
|
||||
Illuminate\Session\SessionServiceProvider::class,
|
||||
Illuminate\Translation\TranslationServiceProvider::class,
|
||||
Illuminate\Validation\ValidationServiceProvider::class,
|
||||
Illuminate\View\ViewServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Package Service Providers...
|
||||
*/
|
||||
|
||||
//
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
|
||||
Laravel\Scout\ScoutServiceProvider::class,
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Class Aliases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This array of class aliases will be registered when this application
|
||||
| is started. However, feel free to register as many as you wish as
|
||||
| the aliases are "lazy" loaded so they don't hinder performance.
|
||||
|
|
||||
*/
|
||||
|
||||
'aliases' => [
|
||||
|
||||
'App' => Illuminate\Support\Facades\App::class,
|
||||
'Artisan' => Illuminate\Support\Facades\Artisan::class,
|
||||
'Auth' => Illuminate\Support\Facades\Auth::class,
|
||||
'Blade' => Illuminate\Support\Facades\Blade::class,
|
||||
'Bus' => Illuminate\Support\Facades\Bus::class,
|
||||
'Cache' => Illuminate\Support\Facades\Cache::class,
|
||||
'Config' => Illuminate\Support\Facades\Config::class,
|
||||
'Cookie' => Illuminate\Support\Facades\Cookie::class,
|
||||
'Crypt' => Illuminate\Support\Facades\Crypt::class,
|
||||
'DB' => Illuminate\Support\Facades\DB::class,
|
||||
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
||||
'Event' => Illuminate\Support\Facades\Event::class,
|
||||
'File' => Illuminate\Support\Facades\File::class,
|
||||
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||
'Log' => Illuminate\Support\Facades\Log::class,
|
||||
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||
'Notification' => Illuminate\Support\Facades\Notification::class,
|
||||
'Password' => Illuminate\Support\Facades\Password::class,
|
||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||
'Redis' => Illuminate\Support\Facades\Redis::class,
|
||||
'Request' => Illuminate\Support\Facades\Request::class,
|
||||
'Response' => Illuminate\Support\Facades\Response::class,
|
||||
'Route' => Illuminate\Support\Facades\Route::class,
|
||||
'Schema' => Illuminate\Support\Facades\Schema::class,
|
||||
'Session' => Illuminate\Support\Facades\Session::class,
|
||||
'Storage' => Illuminate\Support\Facades\Storage::class,
|
||||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
|
||||
],
|
||||
|
||||
];
|
102
backend/config/auth.php
Normal file
102
backend/config/auth.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default authentication "guard" and password
|
||||
| reset options for your application. You may change these defaults
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => 'web',
|
||||
'passwords' => 'users',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| here which uses session storage and the Eloquent user provider.
|
||||
|
|
||||
| All authentication drivers have a user provider. This defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| Supported: "session", "token"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'driver' => 'token',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication drivers have a user provider. This defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| sources which represent each model / table. These sources may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\User::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may specify multiple password reset configurations if you have more
|
||||
| than one user table or model in the application and you want to have
|
||||
| separate password reset settings based on the specific user types.
|
||||
|
|
||||
| The expire time is the number of minutes that the reset token should be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => 'password_resets',
|
||||
'expire' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
58
backend/config/broadcasting.php
Normal file
58
backend/config/broadcasting.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Broadcaster
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default broadcaster that will be used by the
|
||||
| framework when an event needs to be broadcast. You may set this to
|
||||
| any of the connections defined in the "connections" array below.
|
||||
|
|
||||
| Supported: "pusher", "redis", "log", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('BROADCAST_DRIVER', 'null'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Broadcast Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the broadcast connections that will be used
|
||||
| to broadcast events to other systems or over websockets. Samples of
|
||||
| each available type of connection are provided inside this array.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'pusher' => [
|
||||
'driver' => 'pusher',
|
||||
'key' => env('PUSHER_KEY'),
|
||||
'secret' => env('PUSHER_SECRET'),
|
||||
'app_id' => env('PUSHER_APP_ID'),
|
||||
'options' => [
|
||||
//
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'driver' => 'log',
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'null',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
91
backend/config/cache.php
Normal file
91
backend/config/cache.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache connection that gets used while
|
||||
| using this caching library. This connection is used when another is
|
||||
| not explicitly specified when executing a given caching function.
|
||||
|
|
||||
| Supported: "apc", "array", "database", "file", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_DRIVER', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'apc' => [
|
||||
'driver' => 'apc',
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'table' => 'cache',
|
||||
'connection' => null,
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing a RAM based store such as APC or Memcached, there might
|
||||
| be other applications utilizing the same cache. So, we'll specify a
|
||||
| value to get prefixed to all our keys so we can avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => 'laravel',
|
||||
|
||||
];
|
35
backend/config/compile.php
Normal file
35
backend/config/compile.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Additional Compiled Classes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify additional classes to include in the compiled file
|
||||
| generated by the `artisan optimize` command. These should be classes
|
||||
| that are included on basically every request into the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => [
|
||||
//
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Compiled File Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may list service providers which define a "compiles" function
|
||||
| that returns additional files that should be compiled, providing an
|
||||
| easy way to get common files from any packages you are utilizing.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
//
|
||||
],
|
||||
|
||||
];
|
121
backend/config/database.php
Normal file
121
backend/config/database.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PDO Fetch Style
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, database results will be returned as instances of the PHP
|
||||
| stdClass object; however, you may desire to retrieve records in an
|
||||
| array format for simplicity. Here you can tweak the fetch style.
|
||||
|
|
||||
*/
|
||||
|
||||
'fetch' => PDO::FETCH_OBJ,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for all database work. Of course
|
||||
| you may use many connections at once using the Database library.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'mysql'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here are each of the database connections setup for your application.
|
||||
| Of course, examples of configuring each database platform that is
|
||||
| supported by Laravel is shown below to make development simple.
|
||||
|
|
||||
|
|
||||
| All database work in Laravel is done through the PHP PDO facilities
|
||||
| so make sure you have the driver for your particular database of
|
||||
| choice installed on your machine before you begin development.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
'prefix' => '',
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'schema' => 'public',
|
||||
'sslmode' => 'prefer',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run in the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => 'migrations',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer set of commands than a typical key-value systems
|
||||
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'cluster' => false,
|
||||
|
||||
'default' => [
|
||||
'host' => env('REDIS_HOST', 'localhost'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => 0,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
67
backend/config/filesystems.php
Normal file
67
backend/config/filesystems.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. A "local" driver, as well as a variety of cloud
|
||||
| based drivers are available for your choosing. Just store away!
|
||||
|
|
||||
| Supported: "local", "ftp", "s3", "rackspace"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => 'local',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cloud Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Many applications store files both locally and in the cloud. For this
|
||||
| reason, you may specify a default "cloud" driver here. This driver
|
||||
| will be bound as the Cloud disk implementation in the container.
|
||||
|
|
||||
*/
|
||||
|
||||
'cloud' => 's3',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure as many filesystem "disks" as you wish, and you
|
||||
| may even configure multiple disks of the same driver. Defaults have
|
||||
| been setup for each driver as an example of the required options.
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => base_path('../public/assets/poster'),
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'visibility' => 'public',
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => 'your-key',
|
||||
'secret' => 'your-secret',
|
||||
'region' => 'your-region',
|
||||
'bucket' => 'your-bucket',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
115
backend/config/mail.php
Normal file
115
backend/config/mail.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mail Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel supports both SMTP and PHP's "mail" function as drivers for the
|
||||
| sending of e-mail. You may specify which one you're using throughout
|
||||
| your application here. By default, Laravel is setup for SMTP mail.
|
||||
|
|
||||
| Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill",
|
||||
| "ses", "sparkpost", "log"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('MAIL_DRIVER', 'smtp'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Host Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may provide the host address of the SMTP server used by your
|
||||
| applications. A default option is provided that is compatible with
|
||||
| the Mailgun mail service which will provide reliable deliveries.
|
||||
|
|
||||
*/
|
||||
|
||||
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Host Port
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the SMTP port used by your application to deliver e-mails to
|
||||
| users of the application. Like the host we have set this value to
|
||||
| stay compatible with the Mailgun e-mail application by default.
|
||||
|
|
||||
*/
|
||||
|
||||
'port' => env('MAIL_PORT', 587),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all e-mails sent by your application to be sent from
|
||||
| the same address. Here, you may specify a name and address that is
|
||||
| used globally for all e-mails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => 'hello@example.com',
|
||||
'name' => 'Example',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| E-Mail Encryption Protocol
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the encryption protocol that should be used when
|
||||
| the application send e-mail messages. A sensible default using the
|
||||
| transport layer security protocol should provide great security.
|
||||
|
|
||||
*/
|
||||
|
||||
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Server Username
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If your SMTP server requires a username for authentication, you should
|
||||
| set it here. This will get used to authenticate with your server on
|
||||
| connection. You may also set the "password" value below this one.
|
||||
|
|
||||
*/
|
||||
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Server Password
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may set the password required by your SMTP server to send out
|
||||
| messages from your application. This will be given to the server on
|
||||
| connection so that the application will be able to send messages.
|
||||
|
|
||||
*/
|
||||
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sendmail System Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "sendmail" driver to send e-mails, we will need to know
|
||||
| the path to where Sendmail lives on this server. A default path has
|
||||
| been provided here, which will work well on most of your systems.
|
||||
|
|
||||
*/
|
||||
|
||||
'sendmail' => '/usr/sbin/sendmail -bs',
|
||||
|
||||
];
|
85
backend/config/queue.php
Normal file
85
backend/config/queue.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Laravel queue API supports a variety of back-ends via an unified
|
||||
| API, giving you convenient access to each back-end using the same
|
||||
| syntax for each one. Here you may set the default queue driver.
|
||||
|
|
||||
| Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_DRIVER', 'sync'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection information for each server that
|
||||
| is used by your application. A default configuration has been added
|
||||
| for each back-end shipped with Laravel. You are free to add more.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'table' => 'jobs',
|
||||
'queue' => 'default',
|
||||
'retry_after' => 90,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => 'localhost',
|
||||
'queue' => 'default',
|
||||
'retry_after' => 90,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => 'your-public-key',
|
||||
'secret' => 'your-secret-key',
|
||||
'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id',
|
||||
'queue' => 'your-queue-name',
|
||||
'region' => 'us-east-1',
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
'queue' => 'default',
|
||||
'retry_after' => 90,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control which database and table are used to store the jobs that
|
||||
| have failed. You may change them to any database / table you wish.
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'database' => env('DB_CONNECTION', 'mysql'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
83
backend/config/scout.php
Normal file
83
backend/config/scout.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Search Engine
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default search connection that gets used while
|
||||
| using Laravel Scout. This connection is used when syncing all models
|
||||
| to the search service. You should adjust this based on your needs.
|
||||
|
|
||||
| Supported: "algolia", "elasticsearch", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SCOUT_DRIVER'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Index Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify a prefix that will be applied to all search index
|
||||
| names used by Scout. This prefix may be useful if you have multiple
|
||||
| "tenants" or applications sharing the same search infrastructure.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('SCOUT_PREFIX', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Data Syncing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to control if the operations that sync your data
|
||||
| with your search engines are queued. When this is set to "true" then
|
||||
| all automatic data syncing will get queued for better performance.
|
||||
|
|
||||
*/
|
||||
|
||||
'queue' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Algolia Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your Algolia settings. Algolia is a cloud hosted
|
||||
| search engine which works great with Scout out of the box. Just plug
|
||||
| in your application ID and admin API key to get started searching.
|
||||
|
|
||||
*/
|
||||
|
||||
'algolia' => [
|
||||
'id' => env('ALGOLIA_APP_ID'),
|
||||
'secret' => env('ALGOLIA_SECRET'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Elasticsearch Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your settings for Elasticsearch, which is a
|
||||
| distributed, open source search and analytics engine. Feel free
|
||||
| to add as many Elasticsearch servers as required by your app.
|
||||
|
|
||||
*/
|
||||
|
||||
'elasticsearch' => [
|
||||
'index' => env('ELASTICSEARCH_INDEX', 'laravel'),
|
||||
|
||||
'config' => [
|
||||
'hosts' => [
|
||||
env('ELASTICSEARCH_HOST', 'localhost'),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
38
backend/config/services.php
Normal file
38
backend/config/services.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Stripe, Mailgun, SparkPost and others. This file provides a sane
|
||||
| default location for this type of information, allowing packages
|
||||
| to have a conventional place to find your various credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'mailgun' => [
|
||||
'domain' => env('MAILGUN_DOMAIN'),
|
||||
'secret' => env('MAILGUN_SECRET'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('SES_KEY'),
|
||||
'secret' => env('SES_SECRET'),
|
||||
'region' => 'us-east-1',
|
||||
],
|
||||
|
||||
'sparkpost' => [
|
||||
'secret' => env('SPARKPOST_SECRET'),
|
||||
],
|
||||
|
||||
'stripe' => [
|
||||
'model' => App\User::class,
|
||||
'key' => env('STRIPE_KEY'),
|
||||
'secret' => env('STRIPE_SECRET'),
|
||||
],
|
||||
|
||||
];
|
179
backend/config/session.php
Normal file
179
backend/config/session.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default session "driver" that will be used on
|
||||
| requests. By default, we will use the lightweight native driver but
|
||||
| you may specify any of the other wonderful drivers provided here.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "apc",
|
||||
| "memcached", "redis", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to immediately expire on the browser closing, set that option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => 120,
|
||||
|
||||
'expire_on_close' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it is stored. All encryption will be run
|
||||
| automatically by Laravel and you can use the Session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the native session driver, we need a location where session
|
||||
| files may be stored. A default has been set for you but a different
|
||||
| location may be specified. This is only needed for file sessions.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table we
|
||||
| should use to manage the sessions. Of course, a sensible default is
|
||||
| provided for you; however, you are free to change this as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => 'sessions',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "apc" or "memcached" session drivers, you may specify a
|
||||
| cache store that should be used for these sessions. This value must
|
||||
| correspond with one of the application's configured cache stores.
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the cookie used to identify a session
|
||||
| instance by ID. The name specified here will get used every time a
|
||||
| new session cookie is created by the framework for every driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => 'laravel_session',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application but you are free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => '/',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the domain of the cookie used to identify a session
|
||||
| in your application. This will determine which domains the cookie is
|
||||
| available to in your application. A sensible default has been set.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you if it can not be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. You are free to modify this option if needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => true,
|
||||
|
||||
];
|
33
backend/config/view.php
Normal file
33
backend/config/view.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| View Storage Paths
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Most templating systems load templates from disk. Here you may specify
|
||||
| an array of paths that should be checked for your views. Of course
|
||||
| the usual Laravel view path has already been registered for you.
|
||||
|
|
||||
*/
|
||||
|
||||
'paths' => [
|
||||
realpath(base_path('../client/resources')),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Compiled View Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines where all the compiled Blade templates will be
|
||||
| stored for your application. Typically, this is within the storage
|
||||
| directory. However, as usual, you are free to change this value.
|
||||
|
|
||||
*/
|
||||
|
||||
'compiled' => realpath(storage_path('framework/views')),
|
||||
|
||||
];
|
1
backend/database/.gitignore
vendored
Normal file
1
backend/database/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.sqlite
|
23
backend/database/factories/ModelFactory.php
Normal file
23
backend/database/factories/ModelFactory.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Model Factories
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of your model factories. Model factories give
|
||||
| you a convenient way to create models for testing and seeding your
|
||||
| database. Just tell the factory how a default model should look.
|
||||
|
|
||||
*/
|
||||
|
||||
/*$factory->define(App\User::class, function (Faker\Generator $faker) {
|
||||
static $password;
|
||||
|
||||
return [
|
||||
'name' => $faker->name,
|
||||
'email' => $faker->unique()->safeEmail,
|
||||
'password' => $password ?: $password = bcrypt('secret'),
|
||||
'remember_token' => str_random(10),
|
||||
];
|
||||
});*/
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateItemsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('items', function(Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('tmdb_id')->unique();
|
||||
$table->string('title')->index();
|
||||
$table->string('alternative_title')->index()->nullable();
|
||||
$table->string('poster');
|
||||
$table->string('rating');
|
||||
$table->integer('released');
|
||||
$table->integer('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('items');
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('username')->unique();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('users');
|
||||
}
|
||||
}
|
1
backend/database/seeds/.gitkeep
Normal file
1
backend/database/seeds/.gitkeep
Normal file
@ -0,0 +1 @@
|
||||
|
16
backend/database/seeds/DatabaseSeeder.php
Normal file
16
backend/database/seeds/DatabaseSeeder.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
// $this->call(UsersTableSeeder::class);
|
||||
}
|
||||
}
|
27
backend/phpunit.xml
Normal file
27
backend/phpunit.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="bootstrap/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Application Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./app</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
</php>
|
||||
</phpunit>
|
0
backend/routes/api.php
Normal file
0
backend/routes/api.php
Normal file
0
backend/routes/console.php
Normal file
0
backend/routes/console.php
Normal file
21
backend/routes/web.php
Normal file
21
backend/routes/web.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
Route::group(['prefix' => 'api'], function() {
|
||||
|
||||
Route::post('/login', 'UserController@login');
|
||||
Route::get('/logout', 'UserController@logout');
|
||||
|
||||
Route::get('/items/{orderBy}', 'ItemController@items');
|
||||
Route::get('/search-items', 'ItemController@search');
|
||||
|
||||
Route::group(['middleware' => 'auth'], function() {
|
||||
Route::get('/search-tmdb', 'TMDBController@search');
|
||||
Route::post('/add', 'ItemController@add');
|
||||
Route::patch('/change-rating/{itemID}', 'ItemController@changeRating');
|
||||
Route::delete('/remove/{itemID}', 'ItemController@remove');
|
||||
});
|
||||
});
|
||||
|
||||
Route::get('/{uri?}', function($uri = null) {
|
||||
return view('app');
|
||||
})->where('uri', '(.*)');
|
21
backend/server.php
Normal file
21
backend/server.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel - A PHP Framework For Web Artisans
|
||||
*
|
||||
* @package Laravel
|
||||
* @author Taylor Otwell <taylor@laravel.com>
|
||||
*/
|
||||
|
||||
$uri = urldecode(
|
||||
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
|
||||
);
|
||||
|
||||
// This file allows us to emulate Apache's "mod_rewrite" functionality from the
|
||||
// built-in PHP web server. This provides a convenient way to test a Laravel
|
||||
// application without having installed a "real" web server software here.
|
||||
if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
require_once __DIR__.'/public/index.php';
|
3
backend/storage/app/.gitignore
vendored
Normal file
3
backend/storage/app/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*
|
||||
!public/
|
||||
!.gitignore
|
2
backend/storage/app/public/.gitignore
vendored
Normal file
2
backend/storage/app/public/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
8
backend/storage/framework/.gitignore
vendored
Normal file
8
backend/storage/framework/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
config.php
|
||||
routes.php
|
||||
schedule-*
|
||||
compiled.php
|
||||
services.json
|
||||
events.scanned.php
|
||||
routes.scanned.php
|
||||
down
|
2
backend/storage/framework/cache/.gitignore
vendored
Normal file
2
backend/storage/framework/cache/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
2
backend/storage/framework/sessions/.gitignore
vendored
Normal file
2
backend/storage/framework/sessions/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
2
backend/storage/framework/views/.gitignore
vendored
Normal file
2
backend/storage/framework/views/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
2
backend/storage/logs/.gitignore
vendored
Normal file
2
backend/storage/logs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
19
backend/tests/ExampleTest.php
Normal file
19
backend/tests/ExampleTest.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
class ExampleTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* A basic functional test example.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBasicExample()
|
||||
{
|
||||
$this->visit('/')
|
||||
->see('Laravel');
|
||||
}
|
||||
}
|
25
backend/tests/TestCase.php
Normal file
25
backend/tests/TestCase.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase
|
||||
{
|
||||
/**
|
||||
* The base URL to use while testing the application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'http://localhost';
|
||||
|
||||
/**
|
||||
* Creates the application.
|
||||
*
|
||||
* @return \Illuminate\Foundation\Application
|
||||
*/
|
||||
public function createApplication()
|
||||
{
|
||||
$app = require __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
|
||||
return $app;
|
||||
}
|
||||
}
|
5
client/.babelrc
Normal file
5
client/.babelrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"presets": ["es2015", "stage-2"],
|
||||
"plugins": ["transform-runtime"],
|
||||
"comments": false
|
||||
}
|
45
client/app/app.js
Normal file
45
client/app/app.js
Normal file
@ -0,0 +1,45 @@
|
||||
require('../resources/sass/app.scss');
|
||||
|
||||
import Vue from 'vue';
|
||||
import { mapActions, mapState } from 'vuex'
|
||||
|
||||
import SiteHeader from './components/Header.vue';
|
||||
import Search from './components/Search.vue';
|
||||
import SiteFooter from './components/Footer.vue';
|
||||
import Login from './components/Login.vue';
|
||||
|
||||
import router from './routes';
|
||||
import store from './store/index';
|
||||
|
||||
const App = new Vue({
|
||||
store,
|
||||
router,
|
||||
|
||||
created() {
|
||||
this.checkForUserColorScheme();
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
colorScheme: state => state.colorScheme
|
||||
})
|
||||
},
|
||||
|
||||
components: {
|
||||
SiteHeader, Search, SiteFooter, Login
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions([ 'setColorScheme' ]),
|
||||
|
||||
checkForUserColorScheme() {
|
||||
if( ! localStorage.getItem('color')) {
|
||||
localStorage.setItem('color', 'light');
|
||||
}
|
||||
|
||||
this.setColorScheme(localStorage.getItem('color'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App.$mount('#app');
|
58
client/app/components/Content/Content.vue
Normal file
58
client/app/components/Content/Content.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<main>
|
||||
<div class="wrap-content" v-if=" ! loading">
|
||||
<Item :item="item" v-for="(item, index) in items" :key="index"></Item>
|
||||
|
||||
<span class="nothing-found" v-if=" ! items.length">No Movies Found</span>
|
||||
|
||||
<div class="load-more-wrap">
|
||||
<span class="load-more" v-if=" ! clickedMoreLoading && paginator" @click="loadMore()">LOAD MORE</span>
|
||||
<span class="loader" v-if="clickedMoreLoading"><i></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="loader fullsize-loader" v-if="loading"><i></i></span>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Item from './Item.vue';
|
||||
import { mapActions, mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
loading: state => state.loading,
|
||||
items: state => state.items,
|
||||
userFilter: state => state.userFilter,
|
||||
clickedMoreLoading: state => state.clickedMoreLoading,
|
||||
paginator: state => state.paginator
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions([ 'loadItems', 'loadMoreItems', 'setSearchTitle' ]),
|
||||
|
||||
fetchData() {
|
||||
this.loadItems(this.userFilter);
|
||||
this.setSearchTitle('');
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
this.loadMoreItems(this.paginator);
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Item
|
||||
},
|
||||
|
||||
watch: {
|
||||
'$route': 'fetchData'
|
||||
}
|
||||
}
|
||||
</script>
|
109
client/app/components/Content/Item.vue
Normal file
109
client/app/components/Content/Item.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<transition mode="out-in" name="fade">
|
||||
<div class="item-wrap">
|
||||
<div class="item-image-wrap">
|
||||
<span v-if="localItem.rating" :class="'item-rating rating-' + localItem.rating" @click="changeRating()">
|
||||
<i class="icon-rating"></i>
|
||||
</span>
|
||||
<span v-if=" ! localItem.rating" class="item-rating item-new" @click="addNewItem()">
|
||||
<span class="loader smallsize-loader" v-if="rated"><i></i></span>
|
||||
<i class="icon-add" v-if=" ! rated"></i>
|
||||
</span>
|
||||
<span class="remove-item" v-if="localItem.rating && auth" @click="removeItem()"><i class="icon-remove"></i></span>
|
||||
<img v-if="localItem.poster" :src="poster" class="item-image" width="185" height="278">
|
||||
<span class="no-image" v-if=" ! localItem.poster"></span>
|
||||
</div>
|
||||
|
||||
<div class="item-content">
|
||||
<span class="item-year">{{ released }}</span>
|
||||
<a :href="'https://www.youtube.com/results?search_query=' + title + ' Trailer'" target="_blank" :title="title" class="item-title">{{ title }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['item'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
localItem: this.item,
|
||||
saveTimeout: null,
|
||||
auth: config.auth,
|
||||
prevRating: null,
|
||||
rated: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
title() {
|
||||
return this.localItem.alternative_title ? this.localItem.alternative_title : this.localItem.title;
|
||||
},
|
||||
|
||||
poster() {
|
||||
if(this.localItem.rating) {
|
||||
return config.poster + this.localItem.poster;
|
||||
}
|
||||
|
||||
return config.posterTMDB + this.localItem.poster;
|
||||
},
|
||||
|
||||
released() {
|
||||
const released = new Date(this.localItem.released * 1000);
|
||||
|
||||
return released.getFullYear();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeRating() {
|
||||
if(this.auth) {
|
||||
clearTimeout(this.saveTimeout);
|
||||
|
||||
this.prevRating = this.localItem.rating;
|
||||
this.localItem.rating = this.prevRating == 3 ? 1 : +this.prevRating + 1;
|
||||
|
||||
this.saveTimeout = setTimeout(() => {
|
||||
this.saveNewRating();
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
saveNewRating() {
|
||||
this.$http.patch(`${config.api}/change-rating/${this.localItem.id}`, {rating: this.localItem.rating}).catch(error => {
|
||||
this.localItem.rating = this.prevRating;
|
||||
alert('Error in saveNewRating()');
|
||||
});
|
||||
},
|
||||
|
||||
addNewItem() {
|
||||
if(this.auth) {
|
||||
this.rated = true;
|
||||
|
||||
this.$http.post(`${config.api}/add`, {item: this.localItem}).then(value => {
|
||||
this.localItem = value.data;
|
||||
}, error => {
|
||||
if(error.status == 409) {
|
||||
alert(this.title + ' already exists!');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
removeItem() {
|
||||
if(this.auth) {
|
||||
const confirm = window.confirm('Are you sure?');
|
||||
|
||||
if(confirm) {
|
||||
this.$http.delete(`${config.api}/remove/${this.localItem.id}`).then(value => {
|
||||
this.localItem.rating = null;
|
||||
}, error => {
|
||||
alert('Error in removeItem()');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
82
client/app/components/Content/SearchContent.vue
Normal file
82
client/app/components/Content/SearchContent.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<main>
|
||||
<div class="wrap-content" v-if=" ! loading">
|
||||
<Item v-for="(item, index) in floxItems" :item="item" :key="index"></Item>
|
||||
<Item v-for="(item, index) in tmdbItems" :item="item" :key="index"></Item>
|
||||
|
||||
<span class="nothing-found" v-if=" ! floxItems.length && ! tmdbItems.length">Nothing Found</span>
|
||||
</div>
|
||||
|
||||
<span class="loader fullsize-loader" v-if="loading"><i></i></span>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Item from './Item.vue';
|
||||
import Helper from '../../helper';
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
mixins: [Helper],
|
||||
|
||||
created() {
|
||||
this.initSearch();
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
floxItems: [],
|
||||
tmdbItems: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
searchTitle: state => state.searchTitle,
|
||||
loading: state => state.loading
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
initSearch() {
|
||||
this.$store.commit('SET_SEARCH_TITLE', this.$route.query.q);
|
||||
this.$store.commit('SET_LOADING', true);
|
||||
this.searchFlox();
|
||||
this.searchTMDB().then(() => {
|
||||
setTimeout(() => {
|
||||
this.$store.commit('SET_LOADING', false);
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
|
||||
searchFlox() {
|
||||
this.$http.get(`${config.api}/search-items?q=${this.searchTitle}`).then(value => {
|
||||
this.floxItems = value.data;
|
||||
});
|
||||
},
|
||||
|
||||
async searchTMDB() {
|
||||
if(config.auth) {
|
||||
await this.$http.get(`${config.api}/search-tmdb?q=${this.searchTitle}`).then(value => {
|
||||
const floxItems = this.floxItems.map(item => item.tmdb_id);
|
||||
this.tmdbItems = value.data.filter(item => ! floxItems.includes(item.tmdb_id));
|
||||
}).catch(error => {
|
||||
alert('Error in searchTMDB(): ' + error);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
Item
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route() {
|
||||
this.scrollToTop();
|
||||
this.initSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
32
client/app/components/Content/Settings.vue
Normal file
32
client/app/components/Content/Settings.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<main>
|
||||
<div class="wrap-content" v-if=" ! loading">
|
||||
<span class="nothing-found">Settings coming soon...</span>
|
||||
</div>
|
||||
|
||||
<span class="loader fullsize-loader" v-if="loading"><i></i></span>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created() {
|
||||
this.fetchUserData();
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
username: ''
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchUserData() {
|
||||
setTimeout(() => {
|
||||
this.loading = false;
|
||||
}, 800);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
41
client/app/components/Footer.vue
Normal file
41
client/app/components/Footer.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<footer v-show=" ! loading">
|
||||
<div class="wrap">
|
||||
<span class="attribution">
|
||||
<a href="https://www.themoviedb.org/" target="_blank">
|
||||
<i class="icon-tmdb"></i>
|
||||
</a>
|
||||
This product uses the TMDb API but is not endorsed or certified by TMDb
|
||||
</span>
|
||||
|
||||
<a class="icon-github" href="https://github.com/devfake/flox" target="_blank"></a>
|
||||
|
||||
<div class="sub-links">
|
||||
<a v-if="auth" :href="settings" class="login-btn">Settings</a>
|
||||
<a v-if="auth" :href="logout" class="login-btn">Logout</a>
|
||||
<a v-if=" ! auth" :href="login" class="login-btn">Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
auth: config.auth,
|
||||
logout: config.api + '/logout',
|
||||
login: config.url + '/login',
|
||||
settings: config.url + '/settings'
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
loading: state => state.loading
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
62
client/app/components/Header.vue
Normal file
62
client/app/components/Header.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<header>
|
||||
<div class="wrap">
|
||||
<router-link to="/" class="logo" >
|
||||
<img src="../../../public/assets/img/logo.png" alt="Flox" width="108" height="32">
|
||||
</router-link>
|
||||
|
||||
<span class="sort-wrap">
|
||||
<i class="icon-sort-time" :class="{active: userFilter == 'created_at'}" @click="setUserFilter('created_at')"></i>
|
||||
<i class="icon-sort-star" :class="{active: userFilter == 'rating'}" @click="setUserFilter('rating')"></i>
|
||||
<span class="icon-constrast" @click="toggleColorScheme()"><i></i></span>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '../store/index';
|
||||
import { mapActions, mapMutations, mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
created() {
|
||||
this.checkForUserFilter();
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState({
|
||||
userFilter: state => state.userFilter,
|
||||
colorScheme: state => state.colorScheme
|
||||
}),
|
||||
root() {
|
||||
return config.uri;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions([ 'setColorScheme', 'loadItems' ]),
|
||||
...mapMutations([ 'SET_USER_FILTER' ]),
|
||||
|
||||
toggleColorScheme() {
|
||||
const color = this.colorScheme == 'light' ? 'dark' : 'light';
|
||||
|
||||
this.setColorScheme(color);
|
||||
},
|
||||
|
||||
checkForUserFilter() {
|
||||
if( ! localStorage.getItem('filter')) {
|
||||
localStorage.setItem('filter', 'created_at');
|
||||
}
|
||||
|
||||
this.SET_USER_FILTER(localStorage.getItem('filter'));
|
||||
},
|
||||
|
||||
setUserFilter(type) {
|
||||
localStorage.setItem('filter', type);
|
||||
this.SET_USER_FILTER(type);
|
||||
this.loadItems(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
55
client/app/components/Login.vue
Normal file
55
client/app/components/Login.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="top-bar"></span>
|
||||
<div class="login-wrap">
|
||||
|
||||
<img src="../../../public/assets/img/logo-login.png" class="logo-login" alt="Flox" width="108" height="32">
|
||||
|
||||
<form class="login-form" @submit.prevent="login()">
|
||||
<input type="text" placeholder="Username" v-model="username" autofocus>
|
||||
<input type="password" placeholder="Password" v-model="password">
|
||||
|
||||
<span class="login-error"><span v-if="error">Wrong username or password</span></span>
|
||||
|
||||
<input type="submit" :class="errorShake ? 'shake-horizontal shake-constant' : ''" value="Sign In">
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created() {
|
||||
document.body.className += ' dark';
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
error: false,
|
||||
errorShake: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
login() {
|
||||
this.error = false;
|
||||
const username = this.username;
|
||||
const password = this.password;
|
||||
|
||||
this.$http.post(`${config.api}/login`, {username, password}).then(value => {
|
||||
window.location.href = config.url;
|
||||
}, error => {
|
||||
this.error = true;
|
||||
this.errorShake = true;
|
||||
|
||||
setTimeout(() => {
|
||||
this.errorShake = false;
|
||||
}, 500);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
70
client/app/components/Search.vue
Normal file
70
client/app/components/Search.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<section class="search-wrap" :class="{sticky: sticky}">
|
||||
<div class="wrap">
|
||||
|
||||
<form class="search-form" @submit.prevent="search()">
|
||||
<router-link to="/"><i @click="scrollToTop()" class="icon-logo-small"></i></router-link>
|
||||
<i class="icon-search"></i>
|
||||
<input type="text" :placeholder="placeholder" v-model="title" class="search-input" autofocus>
|
||||
|
||||
<i class="icon-algolia" v-if="algolia"></i>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Helper from '../helper';
|
||||
|
||||
export default {
|
||||
mixins: [Helper],
|
||||
|
||||
mounted() {
|
||||
this.initSticky();
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
sticky: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
algolia() {
|
||||
return config.scoutDriver == 'algolia' && this.$route.query.q;
|
||||
},
|
||||
|
||||
title: {
|
||||
get() {
|
||||
return this.$store.state.searchTitle;
|
||||
},
|
||||
set(title) {
|
||||
this.$store.commit('SET_SEARCH_TITLE', title);
|
||||
}
|
||||
},
|
||||
|
||||
placeholder() {
|
||||
return config.auth ? 'Search or add movie' : 'Search movie';
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
initSticky() {
|
||||
const height = document.querySelector('header').scrollHeight;
|
||||
|
||||
window.onscroll = () => {
|
||||
this.sticky = document.body.scrollTop + document.documentElement.scrollTop > height;
|
||||
};
|
||||
},
|
||||
|
||||
search() {
|
||||
if(this.title != '') {
|
||||
this.$router.push({
|
||||
path: '/search?q=' + this.title
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
18
client/app/config.js
Normal file
18
client/app/config.js
Normal file
@ -0,0 +1,18 @@
|
||||
import Vue from 'vue';
|
||||
import Resource from 'vue-resource';
|
||||
|
||||
Vue.use(Resource);
|
||||
|
||||
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('content');
|
||||
|
||||
const {url, uri, auth, scoutDriver} = document.body.dataset;
|
||||
|
||||
export default {
|
||||
uri,
|
||||
url,
|
||||
auth,
|
||||
scoutDriver,
|
||||
poster: url + '/assets/poster',
|
||||
posterTMDB: 'http://image.tmdb.org/t/p/w185',
|
||||
api: url + '/api'
|
||||
};
|
23
client/app/helper.js
Normal file
23
client/app/helper.js
Normal file
@ -0,0 +1,23 @@
|
||||
export default {
|
||||
methods: {
|
||||
// http://stackoverflow.com/a/24559613
|
||||
scrollToTop(scrollDuration = 300) {
|
||||
let cosParameter = window.scrollY / 2;
|
||||
let scrollCount = 0;
|
||||
let oldTimestamp = performance.now();
|
||||
|
||||
function step(newTimestamp) {
|
||||
scrollCount += Math.PI / (scrollDuration / (newTimestamp - oldTimestamp));
|
||||
|
||||
if(scrollCount >= Math.PI) window.scrollTo(0, 0);
|
||||
if(window.scrollY === 0) return;
|
||||
|
||||
window.scrollTo(0, Math.round(cosParameter + cosParameter * Math.cos(scrollCount)));
|
||||
oldTimestamp = newTimestamp;
|
||||
window.requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(step);
|
||||
}
|
||||
}
|
||||
}
|
22
client/app/routes.js
Normal file
22
client/app/routes.js
Normal file
@ -0,0 +1,22 @@
|
||||
import Vue from 'vue';
|
||||
import Router from 'vue-router';
|
||||
|
||||
import config from './config';
|
||||
window.config = config;
|
||||
|
||||
import Content from './components/Content/Content.vue';
|
||||
import SearchContent from './components/Content/SearchContent.vue';
|
||||
import Settings from './components/Content/Settings.vue';
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
export default new Router({
|
||||
mode: 'history',
|
||||
base: config.uri,
|
||||
routes: [
|
||||
{ path: '/', component: Content },
|
||||
{ path: '/search', component: SearchContent },
|
||||
{ path: '/settings', component: Settings },
|
||||
{ path: '*', component: Content }
|
||||
]
|
||||
});
|
45
client/app/store/actions.js
Normal file
45
client/app/store/actions.js
Normal file
@ -0,0 +1,45 @@
|
||||
import Vue from 'vue';
|
||||
import Resource from 'vue-resource';
|
||||
|
||||
Vue.use(Resource);
|
||||
|
||||
export function loadItems({commit}, filter) {
|
||||
commit('SET_LOADING', true);
|
||||
Vue.http.get(`${config.api}/items/${filter}`).then(value => {
|
||||
const {data, next_page_url} = value.data;
|
||||
|
||||
commit('SET_ITEMS', data);
|
||||
commit('SET_PAGINATOR', next_page_url);
|
||||
|
||||
setTimeout(() => {
|
||||
commit('SET_LOADING', false);
|
||||
}, 500);
|
||||
}, error => {
|
||||
if(error.status == 404) {
|
||||
window.location.href = config.url;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function loadMoreItems({commit}, next_page_url) {
|
||||
commit('SET_CLICKED_LOADING', true);
|
||||
Vue.http.get(next_page_url).then(value => {
|
||||
const {data, next_page_url} = value.data;
|
||||
|
||||
commit('SET_PAGINATOR', next_page_url);
|
||||
|
||||
setTimeout(() => {
|
||||
commit('PUSH_TO_ITEMS', data);
|
||||
commit('SET_CLICKED_LOADING', false);
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
export function setSearchTitle({commit}, title) {
|
||||
commit('SET_SEARCH_TITLE', title);
|
||||
}
|
||||
|
||||
export function setColorScheme({commit}, color) {
|
||||
localStorage.setItem('color', color);
|
||||
commit('SET_COLOR_SCHEME', color);
|
||||
}
|
21
client/app/store/index.js
Normal file
21
client/app/store/index.js
Normal file
@ -0,0 +1,21 @@
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import * as actions from './actions';
|
||||
import mutations from './mutations';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
items: [],
|
||||
searchTitle: '',
|
||||
userFilter: '',
|
||||
loading: false,
|
||||
clickedMoreLoading: false,
|
||||
paginator: null,
|
||||
colorScheme: ''
|
||||
},
|
||||
mutations,
|
||||
actions
|
||||
});
|
35
client/app/store/mutations.js
Normal file
35
client/app/store/mutations.js
Normal file
@ -0,0 +1,35 @@
|
||||
import * as type from './types';
|
||||
|
||||
export default {
|
||||
[type.SET_SEARCH_TITLE](state, title) {
|
||||
state.searchTitle = title;
|
||||
},
|
||||
|
||||
[type.SET_USER_FILTER](state, filter) {
|
||||
state.userFilter = filter;
|
||||
},
|
||||
|
||||
[type.SET_ITEMS](state, items) {
|
||||
state.items = items;
|
||||
},
|
||||
|
||||
[type.PUSH_TO_ITEMS](state, items) {
|
||||
state.items.push(...items);
|
||||
},
|
||||
|
||||
[type.SET_LOADING](state, loading) {
|
||||
state.loading = loading;
|
||||
},
|
||||
|
||||
[type.SET_PAGINATOR](state, data) {
|
||||
state.paginator = data;
|
||||
},
|
||||
|
||||
[type.SET_CLICKED_LOADING](state, loading) {
|
||||
state.clickedMoreLoading = loading;
|
||||
},
|
||||
|
||||
[type.SET_COLOR_SCHEME](state, color) {
|
||||
state.colorScheme = color;
|
||||
}
|
||||
}
|
8
client/app/store/types.js
Normal file
8
client/app/store/types.js
Normal file
@ -0,0 +1,8 @@
|
||||
export const SET_SEARCH_TITLE = 'SET_SEARCH_TITLE';
|
||||
export const SET_USER_FILTER = 'SET_USER_FILTER';
|
||||
export const SET_ITEMS = 'SET_ITEMS';
|
||||
export const PUSH_TO_ITEMS = 'PUSH_TO_ITEMS';
|
||||
export const SET_LOADING = 'SET_LOADING';
|
||||
export const SET_PAGINATOR = 'SET_PAGINATOR';
|
||||
export const SET_CLICKED_LOADING = 'SET_CLICKED_LOADING';
|
||||
export const SET_COLOR_SCHEME = 'SET_COLOR_SCHEME';
|
19
client/gulpfile.js
Normal file
19
client/gulpfile.js
Normal file
@ -0,0 +1,19 @@
|
||||
const elixir = require('laravel-elixir');
|
||||
|
||||
require('laravel-elixir-vue-2');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Elixir Asset Management
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Elixir provides a clean, fluent API for defining some basic Gulp tasks
|
||||
| for your Laravel application. By default, we are compiling the Sass
|
||||
| file for our application, as well as publishing vendor resources.
|
||||
|
|
||||
*/
|
||||
|
||||
elixir(mix => {
|
||||
mix.sass('app.scss')
|
||||
.webpack('app.js');
|
||||
});
|
34
client/package.json
Normal file
34
client/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
|
||||
"dev": "webpack -w"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.11.6",
|
||||
"vue": "^2.0.1",
|
||||
"vue-resource": "^1.0.3",
|
||||
"vue-router": "^2.0.0",
|
||||
"vuex": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.5.0",
|
||||
"babel-core": "^6.17.0",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-es2015": "^6.16.0",
|
||||
"babel-preset-stage-2": "^6.17.0",
|
||||
"cross-env": "^3.1.1",
|
||||
"css-loader": "^0.25.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.9.0",
|
||||
"lost": "^7.1.0",
|
||||
"postcss-loader": "^0.13.0",
|
||||
"sass-loader": "^4.0.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"vue-html-loader": "^1.2.3",
|
||||
"vue-loader": "^9.5.1",
|
||||
"webpack": "^1.13.2"
|
||||
}
|
||||
}
|
37
client/resources/app.blade.php
Normal file
37
client/resources/app.blade.php
Normal file
@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta id="token" content="{{ csrf_token() }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
|
||||
|
||||
<title>Flox - Collect Your Movie Watch List</title>
|
||||
<link rel="stylesheet" href="{{ url('assets/app.css') }}">
|
||||
<link href="{{ url('assets/favicon.ico?v=3') }}" rel="icon" type="image/x-icon">
|
||||
|
||||
</head>
|
||||
<body
|
||||
data-url="{{ url('/') }}"
|
||||
data-uri="{{ config('app.CLIENT_URI') }}"
|
||||
data-scout-driver="{{ config('scout.driver') }}"
|
||||
data-auth="{{ Auth::check() }}"
|
||||
class="{{ Auth::check() ? 'logged' : 'guest' }}"
|
||||
>
|
||||
|
||||
<div id="app" :class="colorScheme">
|
||||
@if(Request::is('login'))
|
||||
<login></login>
|
||||
@else
|
||||
<site-header></site-header>
|
||||
<search></search>
|
||||
<router-view></router-view>
|
||||
<site-footer></site-footer>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<script src="{{ url('assets/vendor.js') }}"></script>
|
||||
<script src="{{ url('assets/app.js') }}"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
46
client/resources/sass/_base.scss
vendored
Normal file
46
client/resources/sass/_base.scss
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-y: scroll;
|
||||
background: #fff;
|
||||
|
||||
&.dark {
|
||||
background: #1c1c1c;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: webkit placeholder opacity
|
||||
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
|
||||
.wrap,
|
||||
.wrap-content {
|
||||
lost-center: 1300px 20px;
|
||||
}
|
||||
|
||||
.wrap-content {
|
||||
@include media(1) { lost-center: 1120px 20px; }
|
||||
@include media(2) { lost-center: 960px 20px; }
|
||||
@include media(3) { lost-center: 800px 20px; }
|
||||
@include media(4) { lost-center: 620px 20px; }
|
||||
@include media(6) { lost-center: 290px 20px; }
|
||||
}
|
||||
|
||||
input,
|
||||
a {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: rgba($main1, .99);
|
||||
color: #fff;
|
||||
}
|
153
client/resources/sass/_misc.scss
vendored
Normal file
153
client/resources/sass/_misc.scss
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
$main1: #895bff;
|
||||
$main2: #f1309a;
|
||||
$dark: #484848;
|
||||
$rating1: #6bb01a;
|
||||
$rating2: #da9527;
|
||||
$rating3: #de2020;
|
||||
|
||||
@mixin transition($type, $type2: '') {
|
||||
@if $type2 == '' {
|
||||
transition: $type .2s ease 0s;
|
||||
}
|
||||
@else {
|
||||
transition: $type .2s ease 0s, $type2 .2s ease 0s;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin media($point) {
|
||||
@if $point == 1 {
|
||||
@media (max-width: 1320px) { @content; }
|
||||
}
|
||||
@else if $point == 2 {
|
||||
@media (max-width: 1140px) { @content; }
|
||||
}
|
||||
@else if $point == 3 {
|
||||
@media (max-width: 860px) { @content; }
|
||||
}
|
||||
@else if $point == 4 {
|
||||
@media (max-width: 740px) { @content; }
|
||||
}
|
||||
@else if $point == 5 {
|
||||
@media (max-width: 620px) { @content; }
|
||||
}
|
||||
@else if $point == 6 {
|
||||
@media (max-width: 460px) { @content; }
|
||||
}
|
||||
@else if $point == sticky {
|
||||
@media (min-width: 901px) { @content; }
|
||||
}
|
||||
@else {
|
||||
@media (max-width: $point) { @content; }
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 29px;
|
||||
height: 29px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
border: 4px solid rgb(241,48,154);
|
||||
animation: cssload-loader 2.3s infinite ease;
|
||||
|
||||
.dark & {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
i {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
background-color: rgb(241,48,154);
|
||||
animation: cssload-loader-inner 2.3s infinite ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
.fullsize-loader {
|
||||
position: absolute;
|
||||
left: calc(50% - 14px);
|
||||
top: calc(50% - 14px);
|
||||
}
|
||||
|
||||
.smallsize-loader {
|
||||
border: 4px solid #fff;
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
margin: 15px auto;
|
||||
|
||||
i {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cssload-loader {
|
||||
0% { transform: rotate(0deg); }
|
||||
25% { transform: rotate(180deg); }
|
||||
50% { transform: rotate(180deg); }
|
||||
75% { transform: rotate(360deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes cssload-loader-inner {
|
||||
0% { height: 0; }
|
||||
25% { height: 0; }
|
||||
50% { height: 100%; }
|
||||
75% { height: 100%; }
|
||||
100% { height: 0; }
|
||||
}
|
||||
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/K88pR3goAWT7BTt32Z01mxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/RjgO7rYTmqiVp7vzi-Q5URJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/LWCjsQkB6EMdfHrEVqA1KRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/xozscpT2726on7jbcb_pAhJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/59ZRklaO5bWGqF5A9baEERJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/u-WUoqrET9fUeobQW7jkRRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), local('OpenSans'), url(https://fonts.gstatic.com/s/opensans/v13/cJZKeOuBrn4kERxqtaUH3VtXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
419
client/resources/sass/_normalize.scss
vendored
Normal file
419
client/resources/sass/_normalize.scss
vendored
Normal file
@ -0,0 +1,419 @@
|
||||
/*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/**
|
||||
* 1. Change the default font family in all browsers (opinionated).
|
||||
* 2. Prevent adjustments of font size after orientation changes in IE and iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
* 2. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details, /* 1 */
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
main, /* 2 */
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template, /* 1 */
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the outline on focused links when they are also active or hovered
|
||||
* in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change font properties to `inherit` in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font: inherit; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the font weight unset by the previous rule.
|
||||
*/
|
||||
|
||||
optgroup {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the border, margin, and padding in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on OS X.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the text style of placeholders in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
color: inherit;
|
||||
opacity: 0.54;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
129
client/resources/sass/_shake.scss
vendored
Normal file
129
client/resources/sass/_shake.scss
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * *\
|
||||
CSShake :: shake-horizontal
|
||||
v1.5.0
|
||||
CSS classes to move your DOM
|
||||
(c) 2015 @elrumordelaluz
|
||||
http://elrumordelaluz.github.io/csshake/
|
||||
Licensed under MIT
|
||||
\* * * * * * * * * * * * * * * * * * * * */
|
||||
.shake-horizontal {
|
||||
display: inline-block;
|
||||
transform-origin: center center; }
|
||||
|
||||
.shake-freeze,
|
||||
.shake-constant.shake-constant--hover:hover,
|
||||
.shake-trigger:hover .shake-constant.shake-constant--hover {
|
||||
animation-play-state: paused; }
|
||||
|
||||
.shake-freeze:hover,
|
||||
.shake-trigger:hover .shake-freeze, .shake-horizontal:hover,
|
||||
.shake-trigger:hover .shake-horizontal {
|
||||
animation-play-state: running; }
|
||||
|
||||
@keyframes shake-horizontal {
|
||||
2% {
|
||||
transform: translate(-2px, 0) rotate(0); }
|
||||
4% {
|
||||
transform: translate(-8px, 0) rotate(0); }
|
||||
6% {
|
||||
transform: translate(7px, 0) rotate(0); }
|
||||
8% {
|
||||
transform: translate(3px, 0) rotate(0); }
|
||||
10% {
|
||||
transform: translate(-6px, 0) rotate(0); }
|
||||
12% {
|
||||
transform: translate(0px, 0) rotate(0); }
|
||||
14% {
|
||||
transform: translate(-9px, 0) rotate(0); }
|
||||
16% {
|
||||
transform: translate(-2px, 0) rotate(0); }
|
||||
18% {
|
||||
transform: translate(3px, 0) rotate(0); }
|
||||
20% {
|
||||
transform: translate(0px, 0) rotate(0); }
|
||||
22% {
|
||||
transform: translate(9px, 0) rotate(0); }
|
||||
24% {
|
||||
transform: translate(-5px, 0) rotate(0); }
|
||||
26% {
|
||||
transform: translate(6px, 0) rotate(0); }
|
||||
28% {
|
||||
transform: translate(5px, 0) rotate(0); }
|
||||
30% {
|
||||
transform: translate(4px, 0) rotate(0); }
|
||||
32% {
|
||||
transform: translate(-5px, 0) rotate(0); }
|
||||
34% {
|
||||
transform: translate(9px, 0) rotate(0); }
|
||||
36% {
|
||||
transform: translate(1px, 0) rotate(0); }
|
||||
38% {
|
||||
transform: translate(7px, 0) rotate(0); }
|
||||
40% {
|
||||
transform: translate(0px, 0) rotate(0); }
|
||||
42% {
|
||||
transform: translate(2px, 0) rotate(0); }
|
||||
44% {
|
||||
transform: translate(-3px, 0) rotate(0); }
|
||||
46% {
|
||||
transform: translate(10px, 0) rotate(0); }
|
||||
48% {
|
||||
transform: translate(-3px, 0) rotate(0); }
|
||||
50% {
|
||||
transform: translate(10px, 0) rotate(0); }
|
||||
52% {
|
||||
transform: translate(-3px, 0) rotate(0); }
|
||||
54% {
|
||||
transform: translate(-5px, 0) rotate(0); }
|
||||
56% {
|
||||
transform: translate(6px, 0) rotate(0); }
|
||||
58% {
|
||||
transform: translate(-4px, 0) rotate(0); }
|
||||
60% {
|
||||
transform: translate(10px, 0) rotate(0); }
|
||||
62% {
|
||||
transform: translate(6px, 0) rotate(0); }
|
||||
64% {
|
||||
transform: translate(-3px, 0) rotate(0); }
|
||||
66% {
|
||||
transform: translate(1px, 0) rotate(0); }
|
||||
68% {
|
||||
transform: translate(-5px, 0) rotate(0); }
|
||||
70% {
|
||||
transform: translate(3px, 0) rotate(0); }
|
||||
72% {
|
||||
transform: translate(-9px, 0) rotate(0); }
|
||||
74% {
|
||||
transform: translate(-3px, 0) rotate(0); }
|
||||
76% {
|
||||
transform: translate(6px, 0) rotate(0); }
|
||||
78% {
|
||||
transform: translate(-7px, 0) rotate(0); }
|
||||
80% {
|
||||
transform: translate(-3px, 0) rotate(0); }
|
||||
82% {
|
||||
transform: translate(7px, 0) rotate(0); }
|
||||
84% {
|
||||
transform: translate(1px, 0) rotate(0); }
|
||||
86% {
|
||||
transform: translate(1px, 0) rotate(0); }
|
||||
88% {
|
||||
transform: translate(8px, 0) rotate(0); }
|
||||
90% {
|
||||
transform: translate(5px, 0) rotate(0); }
|
||||
92% {
|
||||
transform: translate(10px, 0) rotate(0); }
|
||||
94% {
|
||||
transform: translate(-4px, 0) rotate(0); }
|
||||
96% {
|
||||
transform: translate(7px, 0) rotate(0); }
|
||||
98% {
|
||||
transform: translate(-4px, 0) rotate(0); }
|
||||
0%, 100% {
|
||||
transform: translate(0, 0) rotate(0); } }
|
||||
|
||||
.shake-horizontal:hover,
|
||||
.shake-trigger:hover .shake-horizontal,
|
||||
.shake-horizontal.shake-freeze,
|
||||
.shake-horizontal.shake-constant {
|
||||
animation: shake-horizontal 100ms ease-in-out infinite; }
|
96
client/resources/sass/_sprite.scss
vendored
Normal file
96
client/resources/sass/_sprite.scss
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
SCSS variables are information about icon's compiled state, stored under its original file name
|
||||
|
||||
.icon-home {
|
||||
width: $icon-home-width;
|
||||
}
|
||||
|
||||
The large array-like variables contain all information about a single icon
|
||||
$icon-home: x y offset_x offset_y width height total_width total_height image_path;
|
||||
|
||||
At the bottom of this section, we provide information about the spritesheet itself
|
||||
$spritesheet: width height image $spritesheet-sprites;
|
||||
*/
|
||||
$search-name: 'search';
|
||||
$search-x: 0px;
|
||||
$search-y: 0px;
|
||||
$search-offset-x: 0px;
|
||||
$search-offset-y: 0px;
|
||||
$search-width: 20px;
|
||||
$search-height: 20px;
|
||||
$search-total-width: 20px;
|
||||
$search-total-height: 20px;
|
||||
$search-image: '../public/assets/img/sprite.png';
|
||||
$search: (0px, 0px, 0px, 0px, 20px, 20px, 20px, 20px, '../public/assets/img/sprite.png', 'search', );
|
||||
$spritesheet-width: 20px;
|
||||
$spritesheet-height: 20px;
|
||||
$spritesheet-image: '../public/assets/img/sprite.png';
|
||||
$spritesheet-sprites: ($search, );
|
||||
$spritesheet: (20px, 20px, '../public/assets/img/sprite.png', $spritesheet-sprites, );
|
||||
|
||||
/*
|
||||
The provided mixins are intended to be used with the array-like variables
|
||||
|
||||
.icon-home {
|
||||
@include sprite-width($icon-home);
|
||||
}
|
||||
|
||||
.icon-email {
|
||||
@include sprite($icon-email);
|
||||
}
|
||||
|
||||
Example usage in HTML:
|
||||
|
||||
`display: block` sprite:
|
||||
<div class="icon-home"></div>
|
||||
|
||||
To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class:
|
||||
|
||||
// CSS
|
||||
.icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// HTML
|
||||
<i class="icon icon-home"></i>
|
||||
*/
|
||||
@mixin sprite-width($sprite) {
|
||||
width: nth($sprite, 5);
|
||||
}
|
||||
|
||||
@mixin sprite-height($sprite) {
|
||||
height: nth($sprite, 6);
|
||||
}
|
||||
|
||||
@mixin sprite-position($sprite) {
|
||||
$sprite-offset-x: nth($sprite, 3);
|
||||
$sprite-offset-y: nth($sprite, 4);
|
||||
background-position: $sprite-offset-x $sprite-offset-y;
|
||||
}
|
||||
|
||||
@mixin sprite-image($sprite) {
|
||||
$sprite-image: nth($sprite, 9);
|
||||
background-image: url(#{$sprite-image});
|
||||
}
|
||||
|
||||
@mixin sprite($sprite) {
|
||||
@include sprite-image($sprite);
|
||||
@include sprite-position($sprite);
|
||||
@include sprite-width($sprite);
|
||||
@include sprite-height($sprite);
|
||||
}
|
||||
|
||||
/*
|
||||
The `sprites` mixin generates identical output to the CSS template
|
||||
but can be overridden inside of SCSS
|
||||
|
||||
@include sprites($spritesheet-sprites);
|
||||
*/
|
||||
@mixin sprites($sprites) {
|
||||
@each $sprite in $sprites {
|
||||
$sprite-name: nth($sprite, 10);
|
||||
.#{$sprite-name} {
|
||||
@include sprite($sprite);
|
||||
}
|
||||
}
|
||||
}
|
13
client/resources/sass/app.scss
vendored
Normal file
13
client/resources/sass/app.scss
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
@import
|
||||
|
||||
'normalize',
|
||||
'misc',
|
||||
'sprite',
|
||||
'shake',
|
||||
'base',
|
||||
|
||||
'components/header',
|
||||
'components/search',
|
||||
'components/content',
|
||||
'components/login',
|
||||
'components/footer';
|
262
client/resources/sass/components/_content.scss
vendored
Normal file
262
client/resources/sass/components/_content.scss
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
main {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 110px 0 0 0;
|
||||
min-height: 100vh;
|
||||
|
||||
.dark & {
|
||||
background: #1c1c1c;
|
||||
}
|
||||
}
|
||||
|
||||
.item-wrap {
|
||||
margin: 0 0 60px 0;
|
||||
|
||||
lost-column: 1/6;
|
||||
|
||||
@include media(1) { lost-column: 1/4; }
|
||||
@include media(3) { lost-column: 1/5; }
|
||||
@include media(4) { lost-column: 1/4; }
|
||||
@include media(5) { lost-column: 1/3; }
|
||||
@include media(6) { lost-column: 1/2; }
|
||||
}
|
||||
|
||||
.item-image-wrap {
|
||||
position: relative;
|
||||
float: left;
|
||||
max-height: 278px;
|
||||
|
||||
@include media(3) {
|
||||
width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@include media(6) {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.item-new {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-image {
|
||||
box-shadow: 0 12px 15px 0 rgba(0, 0, 0, .5);
|
||||
|
||||
@include media(3) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.no-image {
|
||||
width: 185px;
|
||||
height: 270px;
|
||||
background: $dark;
|
||||
float: left;
|
||||
box-shadow: 0 5px 10px 0 rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
.item-content {
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin: 20px 0 0 0;
|
||||
|
||||
@include media(5) {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.item-year {
|
||||
float: left;
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
|
||||
.dark & {
|
||||
color: #626262;
|
||||
}
|
||||
}
|
||||
|
||||
.item-title {
|
||||
color: $dark;
|
||||
clear: both;
|
||||
font-size: 17px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
text-decoration: none;
|
||||
float: left;
|
||||
|
||||
.dark & {
|
||||
color: #717171;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $main2;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $main1;
|
||||
}
|
||||
}
|
||||
|
||||
.item-rating {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
transform: translate(-50%, -50%);
|
||||
box-shadow: 0 0 15px 0 rgba(0,0,0,.7);
|
||||
border-radius: 25px;
|
||||
|
||||
.logged & {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translate(-50%, -50%) scale(1.1);
|
||||
}
|
||||
|
||||
@include transition(transform, background);
|
||||
}
|
||||
}
|
||||
|
||||
.rating-1 {
|
||||
background: $rating1;
|
||||
|
||||
.icon-rating {
|
||||
background: url(../../../public/assets/img/rating-1.png);
|
||||
}
|
||||
}
|
||||
.rating-2 {
|
||||
background: $rating2;
|
||||
|
||||
.icon-rating {
|
||||
background: url(../../../public/assets/img/rating-2.png);
|
||||
}
|
||||
}
|
||||
.rating-3 {
|
||||
background: $rating3;
|
||||
|
||||
.icon-rating {
|
||||
background: url(../../../public/assets/img/rating-3.png);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-rating {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item-new {
|
||||
background: $main1;
|
||||
display: none;
|
||||
|
||||
@include media(3) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-add {
|
||||
background: url(../../../public/assets/img/add.png);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.remove-item {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
background: $rating3;
|
||||
padding: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
.dark & {
|
||||
background: darken($rating3, 10%);
|
||||
}
|
||||
|
||||
@include transition(opacity);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@include media(3) {
|
||||
// opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-remove {
|
||||
background: url(../../../public/assets/img/remove.png);
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.fade-enter-active {
|
||||
transition: opacity .5s ease;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.fade-enter {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.box {
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
||||
h2 {
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin: 0 0 30px 0;
|
||||
color: $main1;
|
||||
}
|
||||
}
|
||||
|
||||
.nothing-found {
|
||||
float: left;
|
||||
width: 100%;
|
||||
font-size: 32px;
|
||||
margin: 0 0 30px 0;
|
||||
color: $dark;
|
||||
}
|
||||
|
||||
.load-more-wrap {
|
||||
float: left;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: #e1e1e1;
|
||||
color: $dark;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
|
||||
.dark & {
|
||||
background: #2f2f2f;
|
||||
color: #626262;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
61
client/resources/sass/components/_footer.scss
vendored
Normal file
61
client/resources/sass/components/_footer.scss
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
footer {
|
||||
padding: 40px 0;
|
||||
width: 100%;
|
||||
float: left;
|
||||
background: $main2;
|
||||
background: linear-gradient(to right, $main1, $main2);
|
||||
|
||||
.dark & {
|
||||
opacity: .9;
|
||||
}
|
||||
}
|
||||
|
||||
.attribution {
|
||||
color: #fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.icon-tmdb {
|
||||
background: url(../../../public/assets/img/tmdb.png);
|
||||
width: 139px;
|
||||
height: 18px;
|
||||
float: left;
|
||||
margin: 3px 10px 0 0;
|
||||
|
||||
&:active {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-github {
|
||||
background: url(../../../public/assets/img/github.png);
|
||||
width: 33px;
|
||||
height: 27px;
|
||||
float: right;
|
||||
|
||||
@include media(3) {
|
||||
float: left;
|
||||
clear: both;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-links {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
float: left;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
margin: 20px 20px 0 0;
|
||||
|
||||
&:active {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
92
client/resources/sass/components/_header.scss
vendored
Normal file
92
client/resources/sass/components/_header.scss
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
header {
|
||||
background: $main2;
|
||||
background: linear-gradient(to right, $main1, $main2);
|
||||
width: 100%;
|
||||
padding: 25px 0;
|
||||
|
||||
@include media(5) {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.dark & {
|
||||
opacity: .9;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
float: left;
|
||||
|
||||
@include media(5) {
|
||||
width: 80px;
|
||||
height: auto;
|
||||
margin: 7px 0 0 0;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sort-wrap {
|
||||
float: right;
|
||||
margin: 4px 0 0 0;
|
||||
width: auto;
|
||||
|
||||
.icon-sort-time,
|
||||
.icon-sort-star {
|
||||
float: left;
|
||||
width: 32px;
|
||||
height: 30px;
|
||||
margin: 0 0 0 15px;
|
||||
cursor: pointer;
|
||||
opacity: .5;
|
||||
|
||||
@include transition(opacity);
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-sort-time {
|
||||
background: url(../../../public/assets/img/sort-time.png);
|
||||
}
|
||||
|
||||
.icon-sort-star {
|
||||
background: url(../../../public/assets/img/sort-star.png);
|
||||
}
|
||||
|
||||
.icon-constrast {
|
||||
float: left;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
margin: 0 0 0 30px;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
|
||||
&:active {
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
i {
|
||||
background: darken($dark, 20%);
|
||||
border-radius: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
float: left;
|
||||
|
||||
.dark & {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
61
client/resources/sass/components/_login.scss
vendored
Normal file
61
client/resources/sass/components/_login.scss
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
.login-wrap {
|
||||
lost-center: 320px 20px;
|
||||
|
||||
@include media(4) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
float: left;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: $main2;
|
||||
background: linear-gradient(to right, $main1, $main2);
|
||||
}
|
||||
|
||||
.logo-login {
|
||||
display: block;
|
||||
margin: 30vh auto 50px auto;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
float: left;
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
margin: 0 0 5px 0;
|
||||
background: #333;
|
||||
padding: 12px;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
background: $main2;
|
||||
background: linear-gradient(to right, $main1, $main2);
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
border: 0;
|
||||
text-transform: uppercase;
|
||||
padding: 8px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.login-error {
|
||||
float: left;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
|
||||
span {
|
||||
float: left;
|
||||
color: $rating3;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
104
client/resources/sass/components/_search.scss
vendored
Normal file
104
client/resources/sass/components/_search.scss
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
.search-wrap {
|
||||
float: left;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
box-shadow: 0 0 70px 0 rgba(0, 0, 0, .3);
|
||||
background: #fff;
|
||||
opacity: .97;
|
||||
|
||||
.dark & {
|
||||
background: #2f2f2f;
|
||||
}
|
||||
|
||||
&.sticky {
|
||||
@include media(sticky) {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
float: right;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
@include transition(padding);
|
||||
|
||||
.sticky & {
|
||||
padding: 0 0 0 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-input {
|
||||
float: left;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
font-size: 19px;
|
||||
padding: 12px 0 12px 30px;
|
||||
color: $dark;
|
||||
|
||||
.dark & {
|
||||
color: #989898;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-search {
|
||||
background: url(../../../public/assets/img/search.png);
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 15px;
|
||||
|
||||
@include transition(left);
|
||||
|
||||
.dark & {
|
||||
background: url(../../../public/assets/img/search-dark.png);
|
||||
}
|
||||
|
||||
.sticky & {
|
||||
left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-algolia {
|
||||
background: url(../../../public/assets/img/algolia-white.png);
|
||||
height: 25px;
|
||||
width: 80px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 15px;
|
||||
|
||||
.dark & {
|
||||
background: url(../../../public/assets/img/algolia-dark.png);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-logo-small {
|
||||
background: url(../../../public/assets/img/logo-small.png);
|
||||
width: 32px;
|
||||
height: 25px;
|
||||
opacity: 0;
|
||||
top: 12px;
|
||||
left: -50px;
|
||||
position: absolute;
|
||||
|
||||
@include transition(opacity, left);
|
||||
|
||||
.sticky & {
|
||||
opacity: 1;
|
||||
left: 0;
|
||||
|
||||
.dark & {
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
}
|
70
client/webpack.config.js
Normal file
70
client/webpack.config.js
Normal file
@ -0,0 +1,70 @@
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const lost = require('lost');
|
||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: './app/app.js',
|
||||
vendor: ['vue', 'vue-resource', 'vuex']
|
||||
},
|
||||
output: {
|
||||
path: path.resolve('../public/assets'),
|
||||
filename: 'app.js'
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
vue: 'vue/dist/vue.js'
|
||||
}
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue'
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|svg)$/,
|
||||
loader: 'url',
|
||||
query: {
|
||||
limit: 10000,
|
||||
name: 'img/[name].[ext]',
|
||||
emitFile: false
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loader: ExtractTextPlugin.extract('style', 'css!postcss!sass')
|
||||
}
|
||||
]
|
||||
},
|
||||
postcss() {
|
||||
return [autoprefixer, lost];
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
|
||||
new ExtractTextPlugin('app.css')
|
||||
]
|
||||
};
|
||||
|
||||
if(process.env.NODE_ENV === 'production') {
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.OccurenceOrderPlugin()
|
||||
])
|
||||
}
|
20
public/.htaccess
Normal file
20
public/.htaccess
Normal file
@ -0,0 +1,20 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews
|
||||
</IfModule>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Redirect Trailing Slashes If Not A Folder...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)/$ /$1 [L,R=301]
|
||||
|
||||
# Handle Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
</IfModule>
|
1555
public/assets/app.css
vendored
Normal file
1555
public/assets/app.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6225
public/assets/app.js
Normal file
6225
public/assets/app.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/assets/favicon.ico
Normal file
BIN
public/assets/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user