├── version.txt ├── app ├── Listeners │ └── .gitkeep ├── Policies │ └── .gitkeep ├── Services │ ├── Data │ │ └── LinkDataServices.php │ ├── UrlServices.php │ └── MetaDataService.php ├── Events │ └── Event.php ├── Http │ ├── Requests │ │ └── Request.php │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── AjaxMiddleware.php │ │ ├── RedirectIfAuthenticated.php │ │ └── Authenticate.php │ ├── routes.php │ ├── Controllers │ │ ├── HomeController.php │ │ ├── Controller.php │ │ ├── RedirectController.php │ │ ├── ShortenLinkController.php │ │ └── Auth │ │ │ └── AuthController.php │ └── Kernel.php ├── Models │ ├── Link.php │ └── User.php ├── Jobs │ └── Job.php ├── Console │ ├── Commands │ │ └── Inspire.php │ └── Kernel.php ├── Providers │ ├── AuthServiceProvider.php │ ├── EventServiceProvider.php │ ├── AppServiceProvider.php │ └── RouteServiceProvider.php ├── Exceptions │ └── Handler.php └── Helpers │ └── Helpers.php ├── database ├── seeds │ ├── .gitkeep │ └── DatabaseSeeder.php ├── .gitignore ├── migrations │ ├── .gitkeep │ ├── 2000_01_11_000000_install_or_update_database_tables.php │ ├── 2016_03_14_211254_create_user_tables.php │ ├── 2016_03_14_204223_create_system_tables.php │ └── 2016_03_15_095138_create_new_links_table.php └── factories │ └── ModelFactory.php ├── resources ├── views │ ├── .gitkeep │ ├── errors │ │ ├── 404.blade.php │ │ ├── 403.blade.php │ │ ├── 503.blade.php │ │ └── error.blade.php │ ├── layouts │ │ ├── section │ │ │ ├── header.blade.php │ │ │ └── footer.blade.php │ │ └── base.blade.php │ ├── anonymous.blade.php │ └── home.blade.php ├── assets │ ├── sass │ │ ├── app.scss │ │ └── anon.to.scss │ └── js │ │ └── app.js └── lang │ └── en │ ├── pagination.php │ ├── auth.php │ ├── passwords.php │ └── validation.php ├── bootstrap ├── cache │ └── .gitignore ├── autoload.php └── app.php ├── storage ├── debugbar │ └── .gitignore ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── cache │ └── .gitignore │ ├── views │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── public ├── robots.txt ├── favicon.ico ├── favicon.png ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── .htaccess ├── web.config ├── index.php └── js │ ├── html5shiv.respond.min.js │ └── app.js ├── .gitattributes ├── package.json ├── bower.json ├── .gitignore ├── tests ├── ExampleTest.php └── TestCase.php ├── server.php ├── .env.example ├── readme.md ├── compile.sh ├── compile.bat ├── phpunit.xml ├── gulpfile.js ├── config ├── compile.php ├── services.php ├── view.php ├── broadcasting.php ├── filesystems.php ├── cache.php ├── queue.php ├── auth.php ├── mail.php ├── database.php ├── session.php └── app.php ├── artisan └── composer.json /version.txt: -------------------------------------------------------------------------------- 1 | 1458302446 -------------------------------------------------------------------------------- /app/Listeners/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/Policies/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /database/seeds/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/Services/Data/LinkDataServices.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.less linguist-vendored 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derekcsm/anon.to/master/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derekcsm/anon.to/master/public/favicon.png -------------------------------------------------------------------------------- /app/Events/Event.php: -------------------------------------------------------------------------------- 1 | getMessage() }} 8 | @endsection 9 | -------------------------------------------------------------------------------- /app/Http/Requests/Request.php: -------------------------------------------------------------------------------- 1 | call(UsersTableSeeder::class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anon.to", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "dependencies": { 6 | "jquery": "latest", 7 | "bootstrap-sass": "latest", 8 | "bootswatch": "latest", 9 | "fontawesome": "latest", 10 | "sweetalert": "latest", 11 | "Respond": "latest", 12 | "html5shiv": "latest" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /resources/views/errors/403.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors.error') 2 | 3 | @section('page_title') 4 | @endsection 5 | 6 | @section('error_title') 7 | Error 403: {{ $exception->getMessage() }} 8 | @endsection 9 | 10 | @section('message') 11 |

You shall not pass! Unauthorized access to this page is prohibited.

12 | @endsection 13 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | ['web']], function () { 4 | Route::get('csrf', function () { 5 | return csrf_token(); 6 | })->middleware(['ajax']);; 7 | 8 | Route::post('shorten', 'ShortenLinkController@shorten')->middleware(['ajax', 'throttle:20,1']); 9 | }); 10 | 11 | Route::get('/{key}', 'RedirectController@redirect')->where('key', '[A-Za-z0-9]{6}'); 12 | Route::get('/', 'HomeController@index'); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore files # 2 | /vendor 3 | /node_modules 4 | /bower_components 5 | Homestead.yaml 6 | Homestead.json 7 | schema.sql 8 | composer.phar 9 | composer.lock 10 | .env 11 | *.map 12 | 13 | # OS generated files # 14 | .DS_Store 15 | .DS_Store? 16 | ._* 17 | .Spotlight-V100 18 | .Trashes 19 | ehthumbs.db 20 | Thumbs.db 21 | *.TMP 22 | 23 | # IDE generated files # 24 | /.idea 25 | /.phpstorm.meta.php 26 | /_ide_helper.php 27 | /_ide_helper_models.php 28 | -------------------------------------------------------------------------------- /resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors.error') 2 | 3 | @section('page_title') 4 | @endsection 5 | 6 | @section('content') 7 |
8 |
9 |

Temporarily down for maintenance!

10 |
11 |

The hamsters powering our server are taking a break. They should be back in couple of minutes.

12 |
13 |
14 | @endsection 15 | -------------------------------------------------------------------------------- /tests/ExampleTest.php: -------------------------------------------------------------------------------- 1 | visit('/') 17 | ->see('Laravel 5'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Controllers/HomeController.php: -------------------------------------------------------------------------------- 1 | request->server('QUERY_STRING'); 15 | if (!empty($url)) { 16 | return app(RedirectController::class)->anonymousRedirect($url); 17 | } 18 | 19 | $this->meta->setMeta(env('SITE_META_TITLE')); 20 | 21 | return response(view('home')); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /resources/assets/sass/app.scss: -------------------------------------------------------------------------------- 1 | $font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; 2 | $font-family-heading: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | $headings-font-family: $font-family-heading; 4 | $icon-font-path: "../fonts/"; 5 | $web-font-path: ""; 6 | 7 | @import "../../../bower_components/bootswatch/flatly/variables"; 8 | @import "../../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap"; 9 | @import "../../../bower_components/bootswatch/flatly/bootswatch"; 10 | @import "../../../bower_components/sweetalert/dev/sweetalert"; 11 | @import "anon.to"; -------------------------------------------------------------------------------- /app/Http/Middleware/AjaxMiddleware.php: -------------------------------------------------------------------------------- 1 | environment() != 'local' && !$request->ajax()) { 20 | return response('Not Allowed.', 405); 21 | } 22 | 23 | return $next($request); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Jobs/Job.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Redirect Trailing Slashes If Not A Folder... 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteRule ^(.*)/$ /$1 [L,R=301] 11 | 12 | # Handle Front Controller... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_FILENAME} !-f 15 | RewriteRule ^ index.php [L] 16 | 17 | # Handle Authorization Header 18 | RewriteCond %{HTTP:Authorization} . 19 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 20 | 21 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); 22 | 23 | return $app; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 21 | return redirect('/'); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_ENV=local 2 | APP_DEBUG=true 3 | APP_KEY=A1B2c3d4E5f6G7h8 4 | APP_URL='http://anon.to.local' 5 | 6 | SITE_NAME='Anon.to' 7 | SITE_META_TITLE='Anonymous URL Shortener and Redirect Service' 8 | 9 | DB_HOST=127.0.0.1 10 | DB_PORT=3306 11 | DB_DATABASE=anondb 12 | DB_USERNAME=root 13 | DB_PASSWORD=root 14 | 15 | CACHE_DRIVER=redis 16 | SESSION_DRIVER=apc 17 | QUEUE_DRIVER=redis 18 | CACHE_PREFIX=anonto 19 | 20 | REDIS_HOST=127.0.0.1 21 | REDIS_PASSWORD=null 22 | REDIS_PORT=6379 23 | REDIS_DATABASE=null 24 | 25 | MAIL_DRIVER=smtp 26 | MAIL_HOST=mailtrap.io 27 | MAIL_PORT=2525 28 | MAIL_USERNAME=null 29 | MAIL_PASSWORD=null 30 | MAIL_ENCRYPTION=null 31 | 32 | GOOGLE_ANALYTICS='' -------------------------------------------------------------------------------- /resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | define(App\User::class, function (Faker\Generator $faker) { 15 | return [ 16 | 'name' => $faker->name, 17 | 'email' => $faker->safeEmail, 18 | 'password' => bcrypt(str_random(10)), 19 | 'remember_token' => str_random(10), 20 | ]; 21 | }); 22 | -------------------------------------------------------------------------------- /app/Console/Commands/Inspire.php: -------------------------------------------------------------------------------- 1 | comment(PHP_EOL . Inspiring::quote() . PHP_EOL); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire') 28 | // ->hourly(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | guest()) { 21 | if ($request->ajax() || $request->wantsJson()) { 22 | return response('Unauthorized.', 401); 23 | } else { 24 | return redirect()->guest('login'); 25 | } 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'App\Policies\ModelPolicy', 17 | ]; 18 | 19 | /** 20 | * Register any application authentication / authorization services. 21 | * 22 | * @param \Illuminate\Contracts\Auth\Access\Gate $gate 23 | * @return void 24 | */ 25 | public function boot(GateContract $gate) 26 | { 27 | $this->registerPolicies($gate); 28 | 29 | // 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Passwords must be at least six characters and match the confirmation.', 17 | 'reset' => 'Your password has been reset!', 18 | 'sent' => 'We have e-mailed your password reset link!', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that e-mail address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /database/migrations/2000_01_11_000000_install_or_update_database_tables.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'App\Listeners\EventListener', 18 | ], 19 | ]; 20 | 21 | /** 22 | * Register any other events for your application. 23 | * 24 | * @param \Illuminate\Contracts\Events\Dispatcher $events 25 | * @return void 26 | */ 27 | public function boot(DispatcherContract $events) 28 | { 29 | parent::boot($events); 30 | 31 | // 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # anon.to 2 | [anon.to](https://anon.to) is an anonymous URL redirector and shortener built using [Laravel](https://laravel.com/). 3 | 4 | ### Requirement 5 | - [**PHP**](https://php.net) 5.5.9+ or [HHVM](http://hhvm.com) 3.3+ 6 | - PHP Extensions: openssl, mcrypt and mbstring 7 | - Database server: [MySQL](https://www.mysql.com) or [**MariaDB**](https://mariadb.org) 8 | - [Redis](http://redis.io) Server 9 | - [Composer](https://getcomposer.org) 10 | - [Node.js](https://nodejs.org/) with npm 11 | 12 | ### Installation 13 | * clone the repository: `git clone https://github.com/bhutanio/anon.to.git anon.to` 14 | * create a database 15 | * create configuration env file `.env` refer to `.env.example` 16 | * install: `composer install --no-dev` 17 | * setup database tables: `php artisan migrate` 18 | 19 | ### License 20 | anon.to is open source software licensed under the [MIT license](http://opensource.org/licenses/MIT). 21 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Assets Compiler 3 | echo "Make sure you have gulp and yuicompressor node package installed" 4 | echo "If you dont have, install it by npm -g install gulp yuicompressor" 5 | echo "-----------------------------------------------------------" 6 | echo "--- Updating NPM and BOWER" 7 | echo "-----------------------------------------------------------" 8 | npm update -g 9 | bower update 10 | npm update 11 | echo "-----------------------------------------------------------" 12 | echo "--- gulping it up" 13 | echo "-----------------------------------------------------------" 14 | gulp --production 15 | echo "-----------------------------------------------------------" 16 | echo "--- Minifying using yuicompressor" 17 | yuicompressor --type js public/js/app.js -o public/js/app.js 18 | yuicompressor --type css public/css/style.css -o public/css/style.css 19 | echo "--- Completed!" 20 | echo "-----------------------------------------------------------" -------------------------------------------------------------------------------- /compile.bat: -------------------------------------------------------------------------------- 1 | @ECHO off 2 | ECHO Make sure you have gulp and yuicompressor node package installed 3 | ECHO If you dont have, install it by npm -g install gulp yuicompressor 4 | ECHO ----------------------------------------------------------- 5 | ECHO --- Updating NPM and BOWER 6 | ECHO ----------------------------------------------------------- 7 | CALL npm update -g 8 | CALL bower update 9 | CALL npm update 10 | ECHO ----------------------------------------------------------- 11 | ECHO --- gulping it up 12 | ECHO ----------------------------------------------------------- 13 | CALL gulp --production 14 | ECHO ----------------------------------------------------------- 15 | ECHO --- Minifying using yuicompressor 16 | CALL yuicompressor --type js public/js/app.js -o public/js/app.js 17 | CALL yuicompressor --type css public/css/style.css -o public/css/style.css 18 | ECHO --- Completed! 19 | ECHO ----------------------------------------------------------- 20 | EXIT /B %errno% 21 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | request = app('request'); 33 | $this->cache = app('cache'); 34 | $this->meta = app(MetaDataService::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(UrlServices::class); 19 | $this->app->singleton(MetaDataService::class); 20 | } 21 | 22 | /** 23 | * Register any application services. 24 | * 25 | * @return void 26 | */ 27 | public function register() 28 | { 29 | if (config('app.debug') && $this->app->environment() == 'local') { 30 | $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class); 31 | $this->app->register(\Barryvdh\Debugbar\ServiceProvider::class); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/ 14 | 15 | 16 | 17 | 18 | app/ 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var gulp = require('gulp'); 3 | var elixir = require('laravel-elixir'); 4 | 5 | elixir(function (mix) { 6 | mix.sass([ 7 | 'app.scss' 8 | ], './public/css/style.css'); 9 | 10 | mix.scripts([ 11 | './bower_components/bootstrap-sass/assets/javascripts/bootstrap.min.js', 12 | './bower_components/sweetalert/dist/sweetalert.min.js', 13 | './resources/assets/js/**/*.js' 14 | ], './public/js/app.js'); 15 | 16 | mix.scripts([ 17 | './bower_components/html5shiv/dist/html5shiv.min.js', 18 | './bower_components/Respond/dest/respond.min.js' 19 | ], './public/js/html5shiv.respond.min.js'); 20 | 21 | mix.copy('./bower_components/bootstrap-sass/assets/fonts/bootstrap', 'public/fonts/'); 22 | 23 | mix.task('writeVersionFile'); 24 | }); 25 | 26 | gulp.task('writeVersionFile', function (cb) { 27 | var version = Math.floor(Date.now() / 1000); 28 | fs.writeFile('version.txt', version, cb); 29 | }); 30 | -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /resources/views/errors/error.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.base') 2 | 3 | @section('content') 4 |
5 |
6 |

7 | @section('error_title') 8 | {{ meta()->pageTitle() }}: Page not found! 9 | @show 10 |

11 |
12 | @section('message') 13 |

The requested URL was not found on this server. Make sure that the Web site address displayed in the address bar of your browser is spelled and formatted correctly.

14 | @show 15 |

16 | Go Back 17 | Go to Home Page 18 |

19 |
20 |
21 | @endsection -------------------------------------------------------------------------------- /config/compile.php: -------------------------------------------------------------------------------- 1 | [ 17 | // 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled File Providers 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may list service providers which define a "compiles" function 26 | | that returns additional files that should be compiled, providing an 27 | | easy way to get common files from any packages you are utilizing. 28 | | 29 | */ 30 | 31 | 'providers' => [ 32 | // 33 | ], 34 | 35 | ]; 36 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | ], 21 | 22 | 'ses' => [ 23 | 'key' => env('SES_KEY'), 24 | 'secret' => env('SES_SECRET'), 25 | 'region' => 'us-east-1', 26 | ], 27 | 28 | 'sparkpost' => [ 29 | 'secret' => env('SPARKPOST_SECRET'), 30 | ], 31 | 32 | 'stripe' => [ 33 | 'model' => App\User::class, 34 | 'key' => env('STRIPE_KEY'), 35 | 'secret' => env('STRIPE_SECRET'), 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | realpath(base_path('resources/views')), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => realpath(storage_path('framework/views')), 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /resources/views/layouts/section/header.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/autoload.php: -------------------------------------------------------------------------------- 1 | group(['namespace' => $this->namespace], function ($router) { 41 | require app_path('Http/routes.php'); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Controllers/RedirectController.php: -------------------------------------------------------------------------------- 1 | cache->get($key)) { 20 | return $this->anonymousRedirect(url($url)); 21 | } 22 | 23 | $url = '/'; 24 | try { 25 | $link = Link::where('hash', $key)->firstOrFail(); 26 | if ($link) { 27 | $url = $url_services->unParseUrlFromDb($link); 28 | $this->cache->put($key, $url, 60 * 24); 29 | } 30 | } catch (ModelNotFoundException $e) { 31 | abort(404, 'Link not found!'); 32 | } 33 | 34 | return $this->anonymousRedirect(url($url)); 35 | } 36 | 37 | public function anonymousRedirect($url) 38 | { 39 | $url = urldecode($url); 40 | return response()->view('anonymous', compact('url')) 41 | ->setExpires(Carbon::now()->addDays(30)) 42 | ->header('Cache-Control', 'public,max-age=' . (3600 * 24 * 30) . ',s-maxage=' . (3600 * 24 * 30)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/2016_03_14_211254_create_user_tables.php: -------------------------------------------------------------------------------- 1 | increments('id'); 15 | $table->string('username')->unique(); 16 | $table->string('email')->unique(); 17 | $table->string('password'); 18 | $table->rememberToken(); 19 | $table->boolean('forgot')->unsigned()->default(false); 20 | $table->string('forgot_token')->nullable()->default(null); 21 | $table->timestamp('forgot_at')->nullable()->default(null); 22 | $table->timestamps(); 23 | }); 24 | 25 | DB::unprepared("INSERT INTO `users` (`id`, `username`, `email`, `password`, `remember_token`, `forgot`, `forgot_token`, `forgot_at`, `created_at`, `updated_at`) VALUES (NULL, 'anonymous', 'anonymous@anon.to', '', NULL, '0', NULL, NULL, CURRENT_TIME(), CURRENT_TIME())"); 26 | 27 | DB::unprepared("INSERT INTO `users` (`id`, `username`, `email`, `password`, `remember_token`, `forgot`, `forgot_token`, `forgot_at`, `created_at`, `updated_at`) VALUES (NULL, 'admin', 'admin@anon.to', '', NULL, '0', NULL, NULL, CURRENT_TIME(), CURRENT_TIME())"); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | */ 33 | public function down() 34 | { 35 | Schema::drop('users'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/views/layouts/section/footer.blade.php: -------------------------------------------------------------------------------- 1 | 22 | 27 | -------------------------------------------------------------------------------- /resources/views/anonymous.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Redirecting to {{ $url }} 7 | 8 | 11 | 12 | 13 |
14 |
15 |

Redirecting to {{ \Illuminate\Support\Str::limit($url,32) }} 16 |

17 |
18 |
19 | 20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'pusher'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Broadcast Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may define all of the broadcast connections that will be used 24 | | to broadcast events to other systems or over websockets. Samples of 25 | | each available type of connection are provided inside this array. 26 | | 27 | */ 28 | 29 | 'connections' => [ 30 | 31 | 'pusher' => [ 32 | 'driver' => 'pusher', 33 | 'key' => env('PUSHER_KEY'), 34 | 'secret' => env('PUSHER_SECRET'), 35 | 'app_id' => env('PUSHER_APP_ID'), 36 | 'options' => [ 37 | // 38 | ], 39 | ], 40 | 41 | 'redis' => [ 42 | 'driver' => 'redis', 43 | 'connection' => 'default', 44 | ], 45 | 46 | 'log' => [ 47 | 'driver' => 'log', 48 | ], 49 | 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /resources/assets/sass/anon.to.scss: -------------------------------------------------------------------------------- 1 | @function dynamic-text-color($color) { 2 | @if (lightness( $color ) > 40) { 3 | @return darken($color, 40); 4 | } 5 | @else { 6 | @return lighten($color, 40); 7 | } 8 | } 9 | 10 | body { 11 | padding-top: 100px; 12 | padding-bottom: 50px; 13 | font-size: ($font-size-base + 1); 14 | font-weight: 300; 15 | } 16 | 17 | .page-title { 18 | text-align: center; 19 | } 20 | 21 | .block { 22 | display: block; 23 | margin: 40px 0; 24 | padding: 20px 40px 60px 40px; 25 | border: 1px solid lighten($jumbotron-bg, 5%); 26 | background-color: $jumbotron-bg; 27 | :after { 28 | clear: both; 29 | } 30 | } 31 | .block-box { 32 | text-align: center; 33 | min-height: 200px; 34 | padding: 60px 0; 35 | .glyphicon { 36 | font-size: 48px; 37 | padding: 10px; 38 | } 39 | } 40 | 41 | .footer { 42 | padding: 20px 0; 43 | color: dynamic-text-color($navbar-default-bg); 44 | background: $navbar-default-bg; 45 | } 46 | .footer-bottom { 47 | margin: 10px 0; 48 | } 49 | 50 | .glyphicon-spin { 51 | -webkit-animation: spin 1000ms infinite linear; 52 | animation: spin 1000ms infinite linear; 53 | } 54 | @-webkit-keyframes spin { 55 | 0% { 56 | -webkit-transform: rotate(0deg); 57 | transform: rotate(0deg); 58 | } 59 | 100% { 60 | -webkit-transform: rotate(359deg); 61 | transform: rotate(359deg); 62 | } 63 | } 64 | @keyframes spin { 65 | 0% { 66 | -webkit-transform: rotate(0deg); 67 | transform: rotate(0deg); 68 | } 69 | 100% { 70 | -webkit-transform: rotate(359deg); 71 | transform: rotate(359deg); 72 | } 73 | } 74 | 75 | .shorten-output { 76 | margin-top: 20px; 77 | font-size: $font-size-large; 78 | } 79 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | [ 27 | \App\Http\Middleware\EncryptCookies::class, 28 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 29 | \Illuminate\Session\Middleware\StartSession::class, 30 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 31 | \App\Http\Middleware\VerifyCsrfToken::class, 32 | ], 33 | 34 | 'api' => [ 35 | 'throttle:60,1', 36 | ], 37 | ]; 38 | 39 | /** 40 | * The application's route middleware. 41 | * 42 | * These middleware may be assigned to groups or used individually. 43 | * 44 | * @var array 45 | */ 46 | protected $routeMiddleware = [ 47 | 'ajax' => \App\Http\Middleware\AjaxMiddleware::class, 48 | 'auth' => \App\Http\Middleware\Authenticate::class, 49 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 50 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 51 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 52 | ]; 53 | } 54 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 32 | 33 | $status = $kernel->handle( 34 | $input = new Symfony\Component\Console\Input\ArgvInput, 35 | new Symfony\Component\Console\Output\ConsoleOutput 36 | ); 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Shutdown The Application 41 | |-------------------------------------------------------------------------- 42 | | 43 | | Once Artisan has finished running. We will fire off the shutdown events 44 | | so that any final work may be done by the application before we shut 45 | | down the process. This is the last thing to happen to the request. 46 | | 47 | */ 48 | 49 | $kernel->terminate($input, $status); 50 | 51 | exit($status); 52 | -------------------------------------------------------------------------------- /app/Services/UrlServices.php: -------------------------------------------------------------------------------- 1 | null, 17 | "host" => null, 18 | "port" => null, 19 | "user" => null, 20 | "pass" => null, 21 | "path" => null, 22 | "query" => null, 23 | "fragment" => null, 24 | ]; 25 | $parsed = parse_url($url) + $defaults; 26 | if (!empty($parsed['path']) && $parsed['path'] == '/') { 27 | $parsed['path'] = null; 28 | } 29 | 30 | return $parsed; 31 | } 32 | 33 | /** 34 | * @param array $parsed 35 | * @return string 36 | */ 37 | public function unParseUrl(array $parsed) 38 | { 39 | if (empty($parsed['path'])) { 40 | $parsed['path'] = '/'; 41 | } 42 | 43 | $unparsed = $parsed['scheme'] . '://' . $parsed['host']; 44 | $unparsed .= !empty($parsed['port']) ? ':' . $parsed['port'] : ''; 45 | $unparsed .= $parsed['path']; 46 | $unparsed .= !empty($parsed['query']) ? '?' . $parsed['query'] : ''; 47 | $unparsed .= !empty($parsed['fragment']) ? '#' . $parsed['fragment'] : ''; 48 | 49 | return $unparsed; 50 | } 51 | 52 | public function unParseUrlFromDb(Link $link) 53 | { 54 | $segments = [ 55 | "scheme" => $link->url_scheme, 56 | "host" => $link->url_host, 57 | "port" => $link->url_port, 58 | "path" => $link->url_path, 59 | "query" => $link->url_query, 60 | "fragment" => $link->url_fragment, 61 | ]; 62 | 63 | return $this->unParseUrl($segments); 64 | } 65 | } -------------------------------------------------------------------------------- /database/migrations/2016_03_14_204223_create_system_tables.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 15 | $table->string('queue'); 16 | $table->longText('payload'); 17 | $table->tinyInteger('attempts')->unsigned(); 18 | $table->tinyInteger('reserved')->unsigned(); 19 | $table->unsignedInteger('reserved_at')->nullable(); 20 | $table->unsignedInteger('available_at'); 21 | $table->unsignedInteger('created_at'); 22 | $table->index(['queue', 'reserved', 'reserved_at']); 23 | }); 24 | 25 | DB::unprepared('DROP TABLE IF EXISTS `_old_jobs`;'); 26 | 27 | Schema::create('_jobs_failed', function (Blueprint $table) { 28 | $table->increments('id'); 29 | $table->text('connection'); 30 | $table->text('queue'); 31 | $table->longText('payload'); 32 | $table->timestamp('failed_at')->useCurrent(); 33 | }); 34 | 35 | Schema::create('_sessions', function (Blueprint $table) { 36 | $table->string('id')->unique(); 37 | $table->integer('user_id')->nullable(); 38 | $table->string('ip_address', 45)->nullable(); 39 | $table->text('user_agent')->nullable(); 40 | $table->text('payload'); 41 | $table->integer('last_activity'); 42 | }); 43 | } 44 | 45 | /** 46 | * Reverse the migrations. 47 | */ 48 | public function down() 49 | { 50 | Schema::drop('_sessions'); 51 | Schema::drop('_jobs_failed'); 52 | Schema::drop('_jobs'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bhutanio/anon.to", 3 | "description": "anon.to - anonymous url redirector and shortener", 4 | "keywords": [ 5 | "anonymous", 6 | "url", 7 | "redirector", 8 | "shortener" 9 | ], 10 | "license": "MIT", 11 | "type": "project", 12 | "require": { 13 | "php": ">=5.5.9", 14 | "laravel/framework": "5.2.*", 15 | "doctrine/dbal": "2.5.*", 16 | "predis/predis": "1.0.*", 17 | "laravelcollective/html": "5.2.*", 18 | "laracasts/flash": "1.3.*", 19 | "google/recaptcha": "1.1.*" 20 | }, 21 | "require-dev": { 22 | "fzaninotto/faker": "~1.4", 23 | "mockery/mockery": "0.9.*", 24 | "phpunit/phpunit": "~4.0", 25 | "symfony/css-selector": "2.8.*|3.0.*", 26 | "symfony/dom-crawler": "2.8.*|3.0.*", 27 | "barryvdh/laravel-ide-helper": "2.*@dev", 28 | "barryvdh/laravel-debugbar": "2.*@dev" 29 | }, 30 | "autoload": { 31 | "classmap": [ 32 | "database" 33 | ], 34 | "psr-4": { 35 | "App\\": "app/" 36 | }, 37 | "files": [ 38 | "app/Helpers/Helpers.php" 39 | ] 40 | }, 41 | "autoload-dev": { 42 | "classmap": [ 43 | "tests/TestCase.php" 44 | ] 45 | }, 46 | "scripts": { 47 | "post-root-package-install": [ 48 | "php -r \"copy('.env.example', '.env');\"" 49 | ], 50 | "post-create-project-cmd": [ 51 | "php artisan key:generate" 52 | ], 53 | "post-install-cmd": [ 54 | "php artisan clear-compiled", 55 | "php artisan optimize" 56 | ], 57 | "pre-update-cmd": [ 58 | "php artisan clear-compiled" 59 | ], 60 | "post-update-cmd": [ 61 | "php artisan optimize" 62 | ] 63 | }, 64 | "config": { 65 | "preferred-install": "dist" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /resources/views/layouts/base.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ meta()->metaTitle() }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | @yield('header_css') 15 | 16 | 18 | 19 | 20 |
21 |
22 | @include('layouts.section.header') 23 |
24 | 25 | @section('page_title') 26 |

Title

27 | @show 28 | 29 | @yield('content') 30 | 31 | @include('layouts.section.footer') 32 | 33 |
34 | 35 | 36 | 37 | @yield('footer_js') 38 | 39 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | /* 11 | |-------------------------------------------------------------------------- 12 | | Register The Auto Loader 13 | |-------------------------------------------------------------------------- 14 | | 15 | | Composer provides a convenient, automatically generated class loader for 16 | | our application. We just need to utilize it! We'll simply require it 17 | | into the script here so that we don't have to worry about manual 18 | | loading any of our classes later on. It feels nice to relax. 19 | | 20 | */ 21 | 22 | require __DIR__.'/../bootstrap/autoload.php'; 23 | 24 | /* 25 | |-------------------------------------------------------------------------- 26 | | Turn On The Lights 27 | |-------------------------------------------------------------------------- 28 | | 29 | | We need to illuminate PHP development, so let us turn on the lights. 30 | | This bootstraps the framework and gets it ready for use, then it 31 | | will load up this application so that we can run it and send 32 | | the responses back to the browser and delight our users. 33 | | 34 | */ 35 | 36 | $app = require_once __DIR__.'/../bootstrap/app.php'; 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Run The Application 41 | |-------------------------------------------------------------------------- 42 | | 43 | | Once we have the application, we can handle the incoming request 44 | | through the kernel, and send the associated response back to 45 | | the client's browser allowing them to enjoy the creative 46 | | and wonderful application we have prepared for them. 47 | | 48 | */ 49 | 50 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 51 | 52 | $response = $kernel->handle( 53 | $request = Illuminate\Http\Request::capture() 54 | ); 55 | 56 | $response->send(); 57 | 58 | $kernel->terminate($request, $response); 59 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | 'local', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Default Cloud Filesystem Disk 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Many applications store files both locally and in the cloud. For this 26 | | reason, you may specify a default "cloud" driver here. This driver 27 | | will be bound as the Cloud disk implementation in the container. 28 | | 29 | */ 30 | 31 | 'cloud' => 's3', 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Filesystem Disks 36 | |-------------------------------------------------------------------------- 37 | | 38 | | Here you may configure as many filesystem "disks" as you wish, and you 39 | | may even configure multiple disks of the same driver. Defaults have 40 | | been setup for each driver as an example of the required options. 41 | | 42 | */ 43 | 44 | 'disks' => [ 45 | 46 | 'local' => [ 47 | 'driver' => 'local', 48 | 'root' => storage_path('app'), 49 | ], 50 | 51 | 'public' => [ 52 | 'driver' => 'local', 53 | 'root' => storage_path('app/public'), 54 | 'visibility' => 'public', 55 | ], 56 | 57 | 's3' => [ 58 | 'driver' => 's3', 59 | 'key' => 'your-key', 60 | 'secret' => 'your-secret', 61 | 'region' => 'your-region', 62 | 'bucket' => 'your-bucket', 63 | ], 64 | 65 | ], 66 | 67 | ]; 68 | -------------------------------------------------------------------------------- /app/Services/MetaDataService.php: -------------------------------------------------------------------------------- 1 | meta_title = env('SITE_NAME'); 12 | $this->setDefaultMeta(); 13 | } 14 | 15 | public function setMeta($page_title = null, $meta_title = null, $description = null, $icon = null) 16 | { 17 | $this->pageTitle($page_title); 18 | $this->metaTitle($meta_title); 19 | if (empty($meta_title)) { 20 | $this->metaTitle($page_title.' - '.env('SITE_NAME')); 21 | } 22 | $this->description($description); 23 | $this->icon($icon); 24 | } 25 | 26 | public function setTheme($theme = null, $color = null) 27 | { 28 | } 29 | 30 | public function metaTitle($title = null) 31 | { 32 | if ($title) { 33 | $this->meta_title = $title; 34 | } 35 | 36 | return $this->meta_title; 37 | } 38 | 39 | public function pageTitle($title = null) 40 | { 41 | if ($title) { 42 | $this->page_title = $title; 43 | } 44 | 45 | return $this->page_title; 46 | } 47 | 48 | public function description($description = null) 49 | { 50 | if ($description) { 51 | $this->description = $description; 52 | } 53 | 54 | return $this->description; 55 | } 56 | 57 | public function canonical($url = null) 58 | { 59 | if ($url) { 60 | $this->canonical = $url; 61 | } 62 | 63 | return $this->canonical; 64 | } 65 | 66 | public function icon($icon = null) 67 | { 68 | if ($icon) { 69 | $this->icon = $icon; 70 | } 71 | 72 | return $this->icon; 73 | } 74 | 75 | private function setDefaultMeta() 76 | { 77 | switch (request()->getRequestUri()) { 78 | case '/auth/login': 79 | $this->setMeta('Login'); 80 | break; 81 | case '/auth/register': 82 | $this->setMeta('Register'); 83 | break; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | handleTokenMismatch($request); 52 | } 53 | 54 | meta()->setMeta('Error'.(($e instanceof HttpException) ? ' '.$e->getStatusCode() : '')); 55 | return parent::render($request, $e); 56 | } 57 | 58 | /** 59 | * @param \Illuminate\Http\Request $request 60 | * @return mixed 61 | */ 62 | private function handleTokenMismatch($request) 63 | { 64 | if ($request->ajax() || $request->wantsJson()) { 65 | return response(json_encode('Your session has expired. Please refresh the page and try again.'), 401); 66 | } 67 | 68 | flash()->warning('Your session has expired. Please try again.'); 69 | 70 | return redirect()->back()->withInput($request->except('_token')); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /resources/assets/js/app.js: -------------------------------------------------------------------------------- 1 | var BASEURL = $('meta[name=_base_url]').attr('content'); 2 | 3 | var shortenUrl = function () { 4 | $('input[name="short_url"]').on('click', function () { 5 | $(this).select(); 6 | }); 7 | 8 | $('#form_shortener').on('submit', function (e) { 9 | var form = $(this); 10 | 11 | form.find('.form-error').remove(); 12 | form.find(':submit').addClass('disabled').attr('disabled', 'disabled'); 13 | form.find('.shorten-output').append(''); 14 | 15 | $.ajax({ 16 | url: form.attr("action"), 17 | type: 'POST', 18 | data: form.serializeArray(), 19 | dataType: 'json' 20 | }).done(function (data) { 21 | form.find('.input-group').removeClass('has-error'); 22 | if (data && data.url) { 23 | form.find('.short-url-group').removeClass('hidden'); 24 | form.find('input[name="short_url"]').val(data.url); 25 | } 26 | }).fail(function (jqXHR) { 27 | form.find('.short-url-group').addClass('hidden'); 28 | form.find('.input-group').addClass('has-error'); 29 | if ($.type(jqXHR.responseJSON) == 'string') { 30 | form.find('.shorten-output').append('' + jqXHR.responseJSON + ''); 31 | } else if ($.type(jqXHR.responseJSON) == 'object') { 32 | $.each(jqXHR.responseJSON, function (index, value) { 33 | if (value.length != 0) { 34 | form.find('.shorten-output').append('' + value + ''); 35 | } 36 | }); 37 | } else { 38 | form.find('.shorten-output').append('' + jqXHR.statusText + ''); 39 | } 40 | }).always(function () { 41 | form.find('.save-spinner').remove(); 42 | form.find(':submit').removeClass('disabled').removeAttr('disabled'); 43 | }); 44 | e.preventDefault(); 45 | }); 46 | $.ajax({ 47 | url: BASEURL + '/csrf', 48 | type: 'GET' 49 | }).done(function (data) { 50 | $('input[name="_token"]').val(data); 51 | }); 52 | }; -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_DRIVER', 'file'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Cache Stores 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may define all of the cache "stores" for your application as 24 | | well as their drivers. You may even define multiple stores for the 25 | | same cache driver to group types of items stored in your caches. 26 | | 27 | */ 28 | 29 | 'stores' => [ 30 | 31 | 'apc' => [ 32 | 'driver' => 'apc', 33 | ], 34 | 35 | 'array' => [ 36 | 'driver' => 'array', 37 | ], 38 | 39 | 'database' => [ 40 | 'driver' => 'database', 41 | 'table' => 'cache', 42 | 'connection' => null, 43 | ], 44 | 45 | 'file' => [ 46 | 'driver' => 'file', 47 | 'path' => storage_path('framework/cache'), 48 | ], 49 | 50 | 'memcached' => [ 51 | 'driver' => 'memcached', 52 | 'servers' => [ 53 | [ 54 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 55 | 'port' => env('MEMCACHED_PORT', 11211), 56 | 'weight' => 100, 57 | ], 58 | ], 59 | ], 60 | 61 | 'redis' => [ 62 | 'driver' => 'redis', 63 | 'connection' => 'default', 64 | ], 65 | 66 | ], 67 | 68 | /* 69 | |-------------------------------------------------------------------------- 70 | | Cache Key Prefix 71 | |-------------------------------------------------------------------------- 72 | | 73 | | When utilizing a RAM based store such as APC or Memcached, there might 74 | | be other applications utilizing the same cache. So, we'll specify a 75 | | value to get prefixed to all our keys so we can avoid collisions. 76 | | 77 | */ 78 | 79 | 'prefix' => env('CACHE_PREFIX', 'laravel'), 80 | 81 | ]; 82 | -------------------------------------------------------------------------------- /database/migrations/2016_03_15_095138_create_new_links_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id')->unsigned(); 15 | $table->string('hash')->unique(); 16 | $table->string('url_scheme'); 17 | $table->text('url_host'); 18 | $table->text('url_port')->nullable()->default(null); 19 | $table->text('url_path')->nullable()->default(null); 20 | $table->text('url_query')->nullable()->default(null); 21 | $table->text('url_fragment')->nullable()->default(null); 22 | $table->integer('created_by')->nullable()->default(null)->unsigned(); 23 | $table->timestamps(); 24 | }); 25 | 26 | if (!empty(DB::select("SHOW TABLES LIKE '_old_links'"))) { 27 | $urls = \DB::select("SELECT * FROM _old_links"); 28 | foreach ($urls as $url) { 29 | $parsed = parse_url($url->url) + [ 30 | "scheme" => null, 31 | "host" => null, 32 | "port" => null, 33 | "user" => null, 34 | "pass" => null, 35 | "path" => null, 36 | "query" => null, 37 | "fragment" => null, 38 | ]; 39 | $parsed['path'] = ($parsed['path'] == '/') ? null : $parsed['path']; 40 | try { 41 | \DB::table('links')->insert([ 42 | 'hash' => $url->hash, 43 | 'url_scheme' => $parsed['scheme'], 44 | 'url_host' => $parsed['host'], 45 | 'url_port' => $parsed['port'], 46 | 'url_path' => $parsed['path'], 47 | 'url_query' => $parsed['query'], 48 | 'url_fragment' => $parsed['fragment'], 49 | 'created_by' => 1, 50 | ]); 51 | } catch (\Exception $e) { 52 | dump($e->getMessage()); 53 | dump($url->url); 54 | } 55 | } 56 | Schema::drop('_old_links'); 57 | } 58 | } 59 | 60 | /** 61 | * Reverse the migrations. 62 | */ 63 | public function down() 64 | { 65 | Schema::drop('links'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_DRIVER', 'sync'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Queue Connections 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may configure the connection information for each server that 27 | | is used by your application. A default configuration has been added 28 | | for each back-end shipped with Laravel. You are free to add more. 29 | | 30 | */ 31 | 32 | 'connections' => [ 33 | 34 | 'sync' => [ 35 | 'driver' => 'sync', 36 | ], 37 | 38 | 'database' => [ 39 | 'driver' => 'database', 40 | 'table' => '_jobs', 41 | 'queue' => 'default', 42 | 'expire' => 60, 43 | ], 44 | 45 | 'beanstalkd' => [ 46 | 'driver' => 'beanstalkd', 47 | 'host' => 'localhost', 48 | 'queue' => 'default', 49 | 'ttr' => 60, 50 | ], 51 | 52 | 'sqs' => [ 53 | 'driver' => 'sqs', 54 | 'key' => 'your-public-key', 55 | 'secret' => 'your-secret-key', 56 | 'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id', 57 | 'queue' => 'your-queue-name', 58 | 'region' => 'us-east-1', 59 | ], 60 | 61 | 'redis' => [ 62 | 'driver' => 'redis', 63 | 'connection' => 'default', 64 | 'queue' => 'default', 65 | 'expire' => 60, 66 | ], 67 | 68 | ], 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Failed Queue Jobs 73 | |-------------------------------------------------------------------------- 74 | | 75 | | These options configure the behavior of failed queue job logging so you 76 | | can control which database and table are used to store the jobs that 77 | | have failed. You may change them to any database / table you wish. 78 | | 79 | */ 80 | 81 | 'failed' => [ 82 | 'database' => env('DB_CONNECTION', 'mysql'), 83 | 'table' => '_jobs_failed', 84 | ], 85 | 86 | ]; 87 | -------------------------------------------------------------------------------- /resources/views/home.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.base') 2 | 3 | @section('page_title') 4 | @show 5 | 6 | @section('content') 7 |
8 |
9 |

Anonymous URL Shortener

10 |

Create a secure anonymous short link from your url which also hides http referer!

11 | {!! Form::open(['files'=>false, 'url'=>url('shorten'), 'id'=>'form_shortener', 'class' => '', 'role'=>'form']) !!} 12 |
13 | {!! Form::text('url', null, ['class' => 'form-control input-lg', 'placeholder'=>'Paste a link to shorten it']) !!} 14 | 15 | {!! Form::submit('Shorten', ['class'=>'btn btn-lg btn-primary']) !!} 16 | 17 |
18 | 19 |
20 | 24 |
25 | 26 | {!! Form::close() !!} 27 |
28 | 29 |
30 |

Anonymous Redirect

31 |

Do you want to link anonymously to other web sites without sending any referrer?

32 |

Use {{ parse_url(env('APP_URL'), PHP_URL_HOST) }} to de-referer or null-referer your links.

33 |

Just put {{ env('APP_URL') }}/? in front of your links. Eg: 34 | {{ env('APP_URL') }}/?http://www.google.com

35 |
36 | 37 |
38 |

Why use anon.to?

39 |
40 |
41 | 42 |

We are SSL Secured.

43 |
44 |
45 |
46 |
47 | 48 |

We don't keep logs.

49 |
50 |
51 |
52 |
53 | 54 |

We hide your original referrer.

55 |
56 |
57 |
58 |
59 | @endsection 60 | 61 | @section('footer_js') 62 | 67 | @endsection -------------------------------------------------------------------------------- /app/Http/Controllers/ShortenLinkController.php: -------------------------------------------------------------------------------- 1 | url_service = $url_service; 20 | } 21 | 22 | public function shorten() 23 | { 24 | $this->validate($this->request, [ 25 | 'url' => 'required|url', 26 | ], [ 27 | 'url.required' => 'Please paste a link to shorten', 28 | 'url.url' => 'Link must be a valid url starting with http:// or https://', 29 | ]); 30 | 31 | $url = $this->request->get('url'); 32 | $parsed_url = $this->url_service->parseUrl($url); 33 | 34 | if ($parsed_url['host'] == parse_url(env('APP_URL'), PHP_URL_HOST)) { 35 | return response()->json(['url' => url($url)], 200); 36 | } 37 | 38 | if ($hash = $this->urlExists($parsed_url)) { 39 | return response()->json(['url' => url($hash)], 200); 40 | } 41 | 42 | $hash = $this->createUrlHash($parsed_url); 43 | 44 | return response()->json(['url' => url($hash)], 200); 45 | } 46 | 47 | private function urlExists(array $url) 48 | { 49 | $link = Link::where('url_scheme', $url['scheme'])->where('url_host', $url['host']); 50 | 51 | if (!empty($url['port'])) { 52 | $link = $link->where('url_port', $url['port']); 53 | } else { 54 | $link = $link->whereNull('url_port'); 55 | } 56 | 57 | if (!empty($url['path'])) { 58 | $link = $link->where('url_path', $url['path']); 59 | } else { 60 | $link = $link->whereNull('url_path'); 61 | } 62 | 63 | if (!empty($url['query'])) { 64 | $link = $link->where('url_query', $url['query']); 65 | } else { 66 | $link = $link->whereNull('url_query'); 67 | } 68 | 69 | if (!empty($url['fragment'])) { 70 | $link = $link->where('url_fragment', $url['fragment']); 71 | } else { 72 | $link = $link->whereNull('url_fragment'); 73 | } 74 | 75 | $link = $link->first(); 76 | if ($link) { 77 | return $link->hash; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | private function createUrlHash($parsed_url) 84 | { 85 | $hash = $this->generateHash(); 86 | 87 | $link = Link::where('hash', $hash)->first(); 88 | while ($link) { 89 | $hash = $this->generateHash(); 90 | $link = Link::where('hash', $hash)->first(); 91 | } 92 | 93 | $link = Link::create([ 94 | 'hash' => $hash, 95 | 'url_scheme' => $parsed_url['scheme'], 96 | 'url_host' => $parsed_url['host'], 97 | 'url_port' => $parsed_url['port'], 98 | 'url_path' => $parsed_url['path'], 99 | 'url_query' => $parsed_url['query'], 100 | 'url_fragment' => $parsed_url['fragment'], 101 | 'created_by' => 1, 102 | ]); 103 | 104 | $this->cacheLink($link); 105 | 106 | return $hash; 107 | } 108 | 109 | private function cacheLink($link) 110 | { 111 | $this->cache->put($link->hash, $this->url_service->unParseUrlFromDb($link), 60 * 24); 112 | 113 | return $link; 114 | } 115 | 116 | private function generateHash() 117 | { 118 | $hash = Str::random(6); 119 | while (in_array(strtolower($hash), excluded_words())) { 120 | $hash = Str::random(6); 121 | } 122 | 123 | return $hash; 124 | } 125 | } -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => 'users', 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session", "token" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | 44 | 'api' => [ 45 | 'driver' => 'token', 46 | 'provider' => 'users', 47 | ], 48 | ], 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | User Providers 53 | |-------------------------------------------------------------------------- 54 | | 55 | | All authentication drivers have a user provider. This defines how the 56 | | users are actually retrieved out of your database or other storage 57 | | mechanisms used by this application to persist your user's data. 58 | | 59 | | If you have multiple user tables or models you may configure multiple 60 | | sources which represent each model / table. These sources may then 61 | | be assigned to any extra authentication guards you have defined. 62 | | 63 | | Supported: "database", "eloquent" 64 | | 65 | */ 66 | 67 | 'providers' => [ 68 | 'users' => [ 69 | 'driver' => 'eloquent', 70 | 'model' => App\Models\User::class, 71 | ], 72 | 73 | // 'users' => [ 74 | // 'driver' => 'database', 75 | // 'table' => 'users', 76 | // ], 77 | ], 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Resetting Passwords 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Here you may set the options for resetting passwords including the view 85 | | that is your password reset e-mail. You may also set the name of the 86 | | table that maintains all of the reset tokens for your application. 87 | | 88 | | You may specify multiple password reset configurations if you have more 89 | | than one user table or model in the application and you want to have 90 | | separate password reset settings based on the specific user types. 91 | | 92 | | The expire time is the number of minutes that the reset token should be 93 | | considered valid. This security feature keeps tokens short-lived so 94 | | they have less time to be guessed. You may change this as needed. 95 | | 96 | */ 97 | 98 | 'passwords' => [ 99 | 'users' => [ 100 | 'provider' => 'users', 101 | 'email' => 'auth.emails.password', 102 | 'table' => 'password_resets', 103 | 'expire' => 60, 104 | ], 105 | ], 106 | 107 | ]; 108 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_DRIVER', 'smtp'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | SMTP Host Address 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may provide the host address of the SMTP server used by your 27 | | applications. A default option is provided that is compatible with 28 | | the Mailgun mail service which will provide reliable deliveries. 29 | | 30 | */ 31 | 32 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | SMTP Host Port 37 | |-------------------------------------------------------------------------- 38 | | 39 | | This is the SMTP port used by your application to deliver e-mails to 40 | | users of the application. Like the host we have set this value to 41 | | stay compatible with the Mailgun e-mail application by default. 42 | | 43 | */ 44 | 45 | 'port' => env('MAIL_PORT', 587), 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Global "From" Address 50 | |-------------------------------------------------------------------------- 51 | | 52 | | You may wish for all e-mails sent by your application to be sent from 53 | | the same address. Here, you may specify a name and address that is 54 | | used globally for all e-mails that are sent by your application. 55 | | 56 | */ 57 | 58 | 'from' => ['address' => null, 'name' => null], 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | E-Mail Encryption Protocol 63 | |-------------------------------------------------------------------------- 64 | | 65 | | Here you may specify the encryption protocol that should be used when 66 | | the application send e-mail messages. A sensible default using the 67 | | transport layer security protocol should provide great security. 68 | | 69 | */ 70 | 71 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 72 | 73 | /* 74 | |-------------------------------------------------------------------------- 75 | | SMTP Server Username 76 | |-------------------------------------------------------------------------- 77 | | 78 | | If your SMTP server requires a username for authentication, you should 79 | | set it here. This will get used to authenticate with your server on 80 | | connection. You may also set the "password" value below this one. 81 | | 82 | */ 83 | 84 | 'username' => env('MAIL_USERNAME'), 85 | 86 | /* 87 | |-------------------------------------------------------------------------- 88 | | SMTP Server Password 89 | |-------------------------------------------------------------------------- 90 | | 91 | | Here you may set the password required by your SMTP server to send out 92 | | messages from your application. This will be given to the server on 93 | | connection so that the application will be able to send messages. 94 | | 95 | */ 96 | 97 | 'password' => env('MAIL_PASSWORD'), 98 | 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Sendmail System Path 102 | |-------------------------------------------------------------------------- 103 | | 104 | | When using the "sendmail" driver to send e-mails, we will need to know 105 | | the path to where Sendmail lives on this server. A default path has 106 | | been provided here, which will work well on most of your systems. 107 | | 108 | */ 109 | 110 | 'sendmail' => '/usr/sbin/sendmail -bs', 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | PDO::FETCH_CLASS, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Database Connection Name 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may specify which of the database connections below you wish 24 | | to use as your default connection for all database work. Of course 25 | | you may use many connections at once using the Database library. 26 | | 27 | */ 28 | 29 | 'default' => env('DB_CONNECTION', 'mysql'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Database Connections 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here are each of the database connections setup for your application. 37 | | Of course, examples of configuring each database platform that is 38 | | supported by Laravel is shown below to make development simple. 39 | | 40 | | 41 | | All database work in Laravel is done through the PHP PDO facilities 42 | | so make sure you have the driver for your particular database of 43 | | choice installed on your machine before you begin development. 44 | | 45 | */ 46 | 47 | 'connections' => [ 48 | 49 | 'sqlite' => [ 50 | 'driver' => 'sqlite', 51 | 'database' => database_path('database.sqlite'), 52 | 'prefix' => '', 53 | ], 54 | 55 | 'mysql' => [ 56 | 'driver' => 'mysql', 57 | 'host' => env('DB_HOST', 'localhost'), 58 | 'port' => env('DB_PORT', '3306'), 59 | 'database' => env('DB_DATABASE', 'forge'), 60 | 'username' => env('DB_USERNAME', 'forge'), 61 | 'password' => env('DB_PASSWORD', ''), 62 | 'charset' => 'utf8', 63 | 'collation' => 'utf8_unicode_ci', 64 | 'prefix' => '', 65 | 'strict' => false, 66 | 'engine' => null, 67 | ], 68 | 69 | 'pgsql' => [ 70 | 'driver' => 'pgsql', 71 | 'host' => env('DB_HOST', 'localhost'), 72 | 'port' => env('DB_PORT', '5432'), 73 | 'database' => env('DB_DATABASE', 'forge'), 74 | 'username' => env('DB_USERNAME', 'forge'), 75 | 'password' => env('DB_PASSWORD', ''), 76 | 'charset' => 'utf8', 77 | 'prefix' => '', 78 | 'schema' => 'public', 79 | ], 80 | 81 | ], 82 | 83 | /* 84 | |-------------------------------------------------------------------------- 85 | | Migration Repository Table 86 | |-------------------------------------------------------------------------- 87 | | 88 | | This table keeps track of all the migrations that have already run for 89 | | your application. Using this information, we can determine which of 90 | | the migrations on disk haven't actually been run in the database. 91 | | 92 | */ 93 | 94 | 'migrations' => 'migrations', 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Redis Databases 99 | |-------------------------------------------------------------------------- 100 | | 101 | | Redis is an open source, fast, and advanced key-value store that also 102 | | provides a richer set of commands than a typical key-value systems 103 | | such as APC or Memcached. Laravel makes it easy to dig right in. 104 | | 105 | */ 106 | 107 | 'redis' => [ 108 | 109 | 'cluster' => false, 110 | 111 | 'default' => [ 112 | 'host' => env('REDIS_HOST', 'localhost'), 113 | 'password' => env('REDIS_PASSWORD', null), 114 | 'port' => env('REDIS_PORT', 6379), 115 | 'database' => env('REDIS_DATABASE', 0), 116 | ], 117 | 118 | ], 119 | 120 | ]; 121 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'file'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Session Lifetime 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may specify the number of minutes that you wish the session 27 | | to be allowed to remain idle before it expires. If you want them 28 | | to immediately expire on the browser closing, set that option. 29 | | 30 | */ 31 | 32 | 'lifetime' => 120, 33 | 34 | 'expire_on_close' => false, 35 | 36 | /* 37 | |-------------------------------------------------------------------------- 38 | | Session Encryption 39 | |-------------------------------------------------------------------------- 40 | | 41 | | This option allows you to easily specify that all of your session data 42 | | should be encrypted before it is stored. All encryption will be run 43 | | automatically by Laravel and you can use the Session like normal. 44 | | 45 | */ 46 | 47 | 'encrypt' => false, 48 | 49 | /* 50 | |-------------------------------------------------------------------------- 51 | | Session File Location 52 | |-------------------------------------------------------------------------- 53 | | 54 | | When using the native session driver, we need a location where session 55 | | files may be stored. A default has been set for you but a different 56 | | location may be specified. This is only needed for file sessions. 57 | | 58 | */ 59 | 60 | 'files' => storage_path('framework/sessions'), 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Session Database Connection 65 | |-------------------------------------------------------------------------- 66 | | 67 | | When using the "database" or "redis" session drivers, you may specify a 68 | | connection that should be used to manage these sessions. This should 69 | | correspond to a connection in your database configuration options. 70 | | 71 | */ 72 | 73 | 'connection' => null, 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Session Database Table 78 | |-------------------------------------------------------------------------- 79 | | 80 | | When using the "database" session driver, you may specify the table we 81 | | should use to manage the sessions. Of course, a sensible default is 82 | | provided for you; however, you are free to change this as needed. 83 | | 84 | */ 85 | 86 | 'table' => 'sessions', 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Session Sweeping Lottery 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Some session drivers must manually sweep their storage location to get 94 | | rid of old sessions from storage. Here are the chances that it will 95 | | happen on a given request. By default, the odds are 2 out of 100. 96 | | 97 | */ 98 | 99 | 'lottery' => [2, 100], 100 | 101 | /* 102 | |-------------------------------------------------------------------------- 103 | | Session Cookie Name 104 | |-------------------------------------------------------------------------- 105 | | 106 | | Here you may change the name of the cookie used to identify a session 107 | | instance by ID. The name specified here will get used every time a 108 | | new session cookie is created by the framework for every driver. 109 | | 110 | */ 111 | 112 | 'cookie' => 'laravel_session', 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Session Cookie Path 117 | |-------------------------------------------------------------------------- 118 | | 119 | | The session cookie path determines the path for which the cookie will 120 | | be regarded as available. Typically, this will be the root path of 121 | | your application but you are free to change this when necessary. 122 | | 123 | */ 124 | 125 | 'path' => '/', 126 | 127 | /* 128 | |-------------------------------------------------------------------------- 129 | | Session Cookie Domain 130 | |-------------------------------------------------------------------------- 131 | | 132 | | Here you may change the domain of the cookie used to identify a session 133 | | in your application. This will determine which domains the cookie is 134 | | available to in your application. A sensible default has been set. 135 | | 136 | */ 137 | 138 | 'domain' => null, 139 | 140 | /* 141 | |-------------------------------------------------------------------------- 142 | | HTTPS Only Cookies 143 | |-------------------------------------------------------------------------- 144 | | 145 | | By setting this option to true, session cookies will only be sent back 146 | | to the server if the browser has a HTTPS connection. This will keep 147 | | the cookie from being sent to you if it can not be done securely. 148 | | 149 | */ 150 | 151 | 'secure' => false, 152 | 153 | ]; 154 | -------------------------------------------------------------------------------- /resources/lang/en/validation.php: -------------------------------------------------------------------------------- 1 | 'The :attribute must be accepted.', 17 | 'active_url' => 'The :attribute is not a valid URL.', 18 | 'after' => 'The :attribute must be a date after :date.', 19 | 'alpha' => 'The :attribute may only contain letters.', 20 | 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.', 21 | 'alpha_num' => 'The :attribute may only contain letters and numbers.', 22 | 'array' => 'The :attribute must be an array.', 23 | 'before' => 'The :attribute must be a date before :date.', 24 | 'between' => [ 25 | 'numeric' => 'The :attribute must be between :min and :max.', 26 | 'file' => 'The :attribute must be between :min and :max kilobytes.', 27 | 'string' => 'The :attribute must be between :min and :max characters.', 28 | 'array' => 'The :attribute must have between :min and :max items.', 29 | ], 30 | 'boolean' => 'The :attribute field must be true or false.', 31 | 'confirmed' => 'The :attribute confirmation does not match.', 32 | 'date' => 'The :attribute is not a valid date.', 33 | 'date_format' => 'The :attribute does not match the format :format.', 34 | 'different' => 'The :attribute and :other must be different.', 35 | 'digits' => 'The :attribute must be :digits digits.', 36 | 'digits_between' => 'The :attribute must be between :min and :max digits.', 37 | 'distinct' => 'The :attribute field has a duplicate value.', 38 | 'email' => 'The :attribute must be a valid email address.', 39 | 'exists' => 'The selected :attribute is invalid.', 40 | 'filled' => 'The :attribute field is required.', 41 | 'image' => 'The :attribute must be an image.', 42 | 'in' => 'The selected :attribute is invalid.', 43 | 'in_array' => 'The :attribute field does not exist in :other.', 44 | 'integer' => 'The :attribute must be an integer.', 45 | 'ip' => 'The :attribute must be a valid IP address.', 46 | 'json' => 'The :attribute must be a valid JSON string.', 47 | 'max' => [ 48 | 'numeric' => 'The :attribute may not be greater than :max.', 49 | 'file' => 'The :attribute may not be greater than :max kilobytes.', 50 | 'string' => 'The :attribute may not be greater than :max characters.', 51 | 'array' => 'The :attribute may not have more than :max items.', 52 | ], 53 | 'mimes' => 'The :attribute must be a file of type: :values.', 54 | 'min' => [ 55 | 'numeric' => 'The :attribute must be at least :min.', 56 | 'file' => 'The :attribute must be at least :min kilobytes.', 57 | 'string' => 'The :attribute must be at least :min characters.', 58 | 'array' => 'The :attribute must have at least :min items.', 59 | ], 60 | 'not_in' => 'The selected :attribute is invalid.', 61 | 'numeric' => 'The :attribute must be a number.', 62 | 'present' => 'The :attribute field must be present.', 63 | 'regex' => 'The :attribute format is invalid.', 64 | 'required' => 'The :attribute field is required.', 65 | 'required_if' => 'The :attribute field is required when :other is :value.', 66 | 'required_unless' => 'The :attribute field is required unless :other is in :values.', 67 | 'required_with' => 'The :attribute field is required when :values is present.', 68 | 'required_with_all' => 'The :attribute field is required when :values is present.', 69 | 'required_without' => 'The :attribute field is required when :values is not present.', 70 | 'required_without_all' => 'The :attribute field is required when none of :values are present.', 71 | 'same' => 'The :attribute and :other must match.', 72 | 'size' => [ 73 | 'numeric' => 'The :attribute must be :size.', 74 | 'file' => 'The :attribute must be :size kilobytes.', 75 | 'string' => 'The :attribute must be :size characters.', 76 | 'array' => 'The :attribute must contain :size items.', 77 | ], 78 | 'string' => 'The :attribute must be a string.', 79 | 'timezone' => 'The :attribute must be a valid zone.', 80 | 'unique' => 'The :attribute has already been taken.', 81 | 'url' => 'The :attribute format is invalid.', 82 | 83 | /* 84 | |-------------------------------------------------------------------------- 85 | | Custom Validation Language Lines 86 | |-------------------------------------------------------------------------- 87 | | 88 | | Here you may specify custom validation messages for attributes using the 89 | | convention "attribute.rule" to name the lines. This makes it quick to 90 | | specify a specific custom language line for a given attribute rule. 91 | | 92 | */ 93 | 94 | 'custom' => [ 95 | 'attribute-name' => [ 96 | 'rule-name' => 'custom-message', 97 | ], 98 | ], 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Custom Validation Attributes 103 | |-------------------------------------------------------------------------- 104 | | 105 | | The following language lines are used to swap attribute place-holders 106 | | with something more reader friendly such as E-Mail Address instead 107 | | of "email". This simply helps us make messages a little cleaner. 108 | | 109 | */ 110 | 111 | 'attributes' => [], 112 | 113 | ]; 114 | -------------------------------------------------------------------------------- /public/js/html5shiv.respond.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){function n(e,t){var n=e.createElement("p"),a=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",a.insertBefore(n.lastChild,a.firstChild)}function a(){var e=E.elements;return"string"==typeof e?e.split(" "):e}function r(e,t){var n=E.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),E.elements=n+" "+e,c(t)}function i(e){var t=v[e[g]];return t||(t={},y++,e[g]=y,v[y]=t),t}function o(e,n,a){if(n||(n=t),d)return n.createElement(e);a||(a=i(n));var r;return r=a.cache[e]?a.cache[e].cloneNode():p.test(e)?(a.cache[e]=a.createElem(e)).cloneNode():a.createElem(e),!r.canHaveChildren||f.test(e)||r.tagUrn?r:a.frag.appendChild(r)}function s(e,n){if(e||(e=t),d)return e.createDocumentFragment();n=n||i(e);for(var r=n.frag.cloneNode(),o=0,s=a(),l=s.length;l>o;o++)r.createElement(s[o]);return r}function l(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return E.shivMethods?o(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+a().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(E,t.frag)}function c(e){e||(e=t);var a=i(e);return!E.shivCSS||m||a.hasCSS||(a.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),d||l(e,a),e}var m,d,u="3.7.3",h=e.html5||{},f=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,g="_html5shiv",y=0,v={};!function(){try{var e=t.createElement("a");e.innerHTML="",m="hidden"in e,d=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){m=!0,d=!0}}();var E={elements:h.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:u,shivCSS:h.shivCSS!==!1,supportsUnknownElements:d,shivMethods:h.shivMethods!==!1,type:"default",shivDocument:c,createElement:o,createDocumentFragment:s,addElements:r};e.html5=E,c(t),"object"==typeof module&&module.exports&&(module.exports=E)}("undefined"!=typeof window?window:this,document),!function(e){"use strict";e.matchMedia=e.matchMedia||function(e){var t,n=e.documentElement,a=n.firstElementChild||n.firstChild,r=e.createElement("body"),i=e.createElement("div");return i.id="mq-test-1",i.style.cssText="position:absolute;top:-100em",r.style.background="none",r.appendChild(i),function(e){return i.innerHTML='­',n.insertBefore(r,a),t=42===i.offsetWidth,n.removeChild(r),{matches:t,media:e}}}(e.document)}(this),function(e){"use strict";function t(){x(!0)}var n={};e.respond=n,n.update=function(){};var a=[],r=function(){var t=!1;try{t=new e.XMLHttpRequest}catch(n){t=new e.ActiveXObject("Microsoft.XMLHTTP")}return function(){return t}}(),i=function(e,t){var n=r();n&&(n.open("GET",e,!0),n.onreadystatechange=function(){4!==n.readyState||200!==n.status&&304!==n.status||t(n.responseText)},4!==n.readyState&&n.send(null))};if(n.ajax=i,n.queue=a,n.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},n.mediaQueriesSupported=e.matchMedia&&null!==e.matchMedia("only all")&&e.matchMedia("only all").matches,!n.mediaQueriesSupported){var o,s,l,c=e.document,m=c.documentElement,d=[],u=[],h=[],f={},p=30,g=c.getElementsByTagName("head")[0]||m,y=c.getElementsByTagName("base")[0],v=g.getElementsByTagName("link"),E=function(){var e,t=c.createElement("div"),n=c.body,a=m.style.fontSize,r=n&&n.style.fontSize,i=!1;return t.style.cssText="position:absolute;font-size:1em;width:1em",n||(n=i=c.createElement("body"),n.style.background="none"),m.style.fontSize="100%",n.style.fontSize="100%",n.appendChild(t),i&&m.insertBefore(n,m.firstChild),e=t.offsetWidth,i?m.removeChild(n):n.removeChild(t),m.style.fontSize=a,r&&(n.style.fontSize=r),e=l=parseFloat(e)},x=function(t){var n="clientWidth",a=m[n],r="CSS1Compat"===c.compatMode&&a||c.body[n]||a,i={},f=v[v.length-1],y=(new Date).getTime();if(t&&o&&p>y-o)return e.clearTimeout(s),void(s=e.setTimeout(x,p));o=y;for(var S in d)if(d.hasOwnProperty(S)){var w=d[S],b=w.minw,C=w.maxw,T=null===b,F=null===C,M="em";b&&(b=parseFloat(b)*(b.indexOf(M)>-1?l||E():1)),C&&(C=parseFloat(C)*(C.indexOf(M)>-1?l||E():1)),w.hasquery&&(T&&F||!(T||r>=b)||!(F||C>=r))||(i[w.media]||(i[w.media]=[]),i[w.media].push(u[w.rules]))}for(var $ in h)h.hasOwnProperty($)&&h[$]&&h[$].parentNode===g&&g.removeChild(h[$]);h.length=0;for(var z in i)if(i.hasOwnProperty(z)){var k=c.createElement("style"),N=i[z].join("\n");k.type="text/css",k.media=z,g.insertBefore(k,f.nextSibling),k.styleSheet?k.styleSheet.cssText=N:k.appendChild(c.createTextNode(N)),h.push(k)}},S=function(e,t,a){var r=e.replace(n.regex.keyframes,"").match(n.regex.media),i=r&&r.length||0;t=t.substring(0,t.lastIndexOf("/"));var o=function(e){return e.replace(n.regex.urls,"$1"+t+"$2$3")},s=!i&&a;t.length&&(t+="/"),s&&(i=1);for(var l=0;i>l;l++){var c,m,h,f;s?(c=a,u.push(o(e))):(c=r[l].match(n.regex.findStyles)&&RegExp.$1,u.push(RegExp.$2&&o(RegExp.$2))),h=c.split(","),f=h.length;for(var p=0;f>p;p++)m=h[p],d.push({media:m.split("(")[0].match(n.regex.only)&&RegExp.$2||"all",rules:u.length-1,hasquery:m.indexOf("(")>-1,minw:m.match(n.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:m.match(n.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}x()},w=function(){if(a.length){var t=a.shift();i(t.href,function(n){S(n,t.href,t.media),f[t.href]=!0,e.setTimeout(function(){w()},0)})}},b=function(){for(var t=0;t env('APP_ENV', 'production'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Debug Mode 21 | |-------------------------------------------------------------------------- 22 | | 23 | | When your application is in debug mode, detailed error messages with 24 | | stack traces will be shown on every error that occurs within your 25 | | application. If disabled, a simple generic error page is shown. 26 | | 27 | */ 28 | 29 | 'debug' => env('APP_DEBUG', false), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application URL 34 | |-------------------------------------------------------------------------- 35 | | 36 | | This URL is used by the console to properly generate URLs when using 37 | | the Artisan command line tool. You should set this to the root of 38 | | your application so that it is used when running Artisan tasks. 39 | | 40 | */ 41 | 42 | 'url' => env('APP_URL', 'http://localhost'), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application Timezone 47 | |-------------------------------------------------------------------------- 48 | | 49 | | Here you may specify the default timezone for your application, which 50 | | will be used by the PHP date and date-time functions. We have gone 51 | | ahead and set this to a sensible default for you out of the box. 52 | | 53 | */ 54 | 55 | 'timezone' => 'UTC', 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Application Locale Configuration 60 | |-------------------------------------------------------------------------- 61 | | 62 | | The application locale determines the default locale that will be used 63 | | by the translation service provider. You are free to set this value 64 | | to any of the locales which will be supported by the application. 65 | | 66 | */ 67 | 68 | 'locale' => 'en', 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Application Fallback Locale 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The fallback locale determines the locale to use when the current one 76 | | is not available. You may change the value to correspond to any of 77 | | the language folders that are provided through your application. 78 | | 79 | */ 80 | 81 | 'fallback_locale' => 'en', 82 | 83 | /* 84 | |-------------------------------------------------------------------------- 85 | | Encryption Key 86 | |-------------------------------------------------------------------------- 87 | | 88 | | This key is used by the Illuminate encrypter service and should be set 89 | | to a random, 32 character string, otherwise these encrypted strings 90 | | will not be safe. Please do this before deploying an application! 91 | | 92 | */ 93 | 94 | 'key' => env('APP_KEY'), 95 | 96 | 'cipher' => 'AES-256-CBC', 97 | 98 | /* 99 | |-------------------------------------------------------------------------- 100 | | Logging Configuration 101 | |-------------------------------------------------------------------------- 102 | | 103 | | Here you may configure the log settings for your application. Out of 104 | | the box, Laravel uses the Monolog PHP logging library. This gives 105 | | you a variety of powerful log handlers / formatters to utilize. 106 | | 107 | | Available Settings: "single", "daily", "syslog", "errorlog" 108 | | 109 | */ 110 | 111 | 'log' => env('APP_LOG', 'single'), 112 | 113 | /* 114 | |-------------------------------------------------------------------------- 115 | | Autoloaded Service Providers 116 | |-------------------------------------------------------------------------- 117 | | 118 | | The service providers listed here will be automatically loaded on the 119 | | request to your application. Feel free to add your own services to 120 | | this array to grant expanded functionality to your applications. 121 | | 122 | */ 123 | 124 | 'providers' => [ 125 | 126 | /* 127 | * Laravel Framework Service Providers... 128 | */ 129 | Illuminate\Auth\AuthServiceProvider::class, 130 | Illuminate\Broadcasting\BroadcastServiceProvider::class, 131 | Illuminate\Bus\BusServiceProvider::class, 132 | Illuminate\Cache\CacheServiceProvider::class, 133 | Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, 134 | Illuminate\Cookie\CookieServiceProvider::class, 135 | Illuminate\Database\DatabaseServiceProvider::class, 136 | Illuminate\Encryption\EncryptionServiceProvider::class, 137 | Illuminate\Filesystem\FilesystemServiceProvider::class, 138 | Illuminate\Foundation\Providers\FoundationServiceProvider::class, 139 | Illuminate\Hashing\HashServiceProvider::class, 140 | Illuminate\Mail\MailServiceProvider::class, 141 | Illuminate\Pagination\PaginationServiceProvider::class, 142 | Illuminate\Pipeline\PipelineServiceProvider::class, 143 | Illuminate\Queue\QueueServiceProvider::class, 144 | Illuminate\Redis\RedisServiceProvider::class, 145 | Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, 146 | Illuminate\Session\SessionServiceProvider::class, 147 | Illuminate\Translation\TranslationServiceProvider::class, 148 | Illuminate\Validation\ValidationServiceProvider::class, 149 | Illuminate\View\ViewServiceProvider::class, 150 | 151 | /* 152 | * Application Service Providers... 153 | */ 154 | App\Providers\AppServiceProvider::class, 155 | App\Providers\AuthServiceProvider::class, 156 | App\Providers\EventServiceProvider::class, 157 | App\Providers\RouteServiceProvider::class, 158 | 159 | Collective\Html\HtmlServiceProvider::class, 160 | Laracasts\Flash\FlashServiceProvider::class, 161 | ], 162 | 163 | /* 164 | |-------------------------------------------------------------------------- 165 | | Class Aliases 166 | |-------------------------------------------------------------------------- 167 | | 168 | | This array of class aliases will be registered when this application 169 | | is started. However, feel free to register as many as you wish as 170 | | the aliases are "lazy" loaded so they don't hinder performance. 171 | | 172 | */ 173 | 174 | 'aliases' => [ 175 | 176 | 'App' => Illuminate\Support\Facades\App::class, 177 | 'Artisan' => Illuminate\Support\Facades\Artisan::class, 178 | 'Auth' => Illuminate\Support\Facades\Auth::class, 179 | 'Blade' => Illuminate\Support\Facades\Blade::class, 180 | 'Cache' => Illuminate\Support\Facades\Cache::class, 181 | 'Config' => Illuminate\Support\Facades\Config::class, 182 | 'Cookie' => Illuminate\Support\Facades\Cookie::class, 183 | 'Crypt' => Illuminate\Support\Facades\Crypt::class, 184 | 'DB' => Illuminate\Support\Facades\DB::class, 185 | 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 186 | 'Event' => Illuminate\Support\Facades\Event::class, 187 | 'File' => Illuminate\Support\Facades\File::class, 188 | 'Gate' => Illuminate\Support\Facades\Gate::class, 189 | 'Hash' => Illuminate\Support\Facades\Hash::class, 190 | 'Lang' => Illuminate\Support\Facades\Lang::class, 191 | 'Log' => Illuminate\Support\Facades\Log::class, 192 | 'Mail' => Illuminate\Support\Facades\Mail::class, 193 | 'Password' => Illuminate\Support\Facades\Password::class, 194 | 'Queue' => Illuminate\Support\Facades\Queue::class, 195 | 'Redirect' => Illuminate\Support\Facades\Redirect::class, 196 | 'Redis' => Illuminate\Support\Facades\Redis::class, 197 | 'Request' => Illuminate\Support\Facades\Request::class, 198 | 'Response' => Illuminate\Support\Facades\Response::class, 199 | 'Route' => Illuminate\Support\Facades\Route::class, 200 | 'Schema' => Illuminate\Support\Facades\Schema::class, 201 | 'Session' => Illuminate\Support\Facades\Session::class, 202 | 'Storage' => Illuminate\Support\Facades\Storage::class, 203 | 'URL' => Illuminate\Support\Facades\URL::class, 204 | 'Validator' => Illuminate\Support\Facades\Validator::class, 205 | 'View' => Illuminate\Support\Facades\View::class, 206 | 207 | 'Form' => Collective\Html\FormFacade::class, 208 | 'Html' => Collective\Html\HtmlFacade::class, 209 | 'Flash' => Laracasts\Flash\Flash::class, 210 | ], 211 | 212 | ]; 213 | -------------------------------------------------------------------------------- /app/Helpers/Helpers.php: -------------------------------------------------------------------------------- 1 | middleware('guest', ['except' => 'logout']); 45 | } 46 | 47 | /** 48 | * Handle a login request to the application. 49 | * 50 | * @param \Illuminate\Http\Request $request 51 | * 52 | * @return \Illuminate\Http\Response 53 | */ 54 | public function login(Request $request) 55 | { 56 | $auth = Auth::guard($this->getGuard()); 57 | 58 | $this->validateLogin($request); 59 | 60 | $throttles = $this->isUsingThrottlesLoginsTrait(); 61 | 62 | if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) { 63 | $this->fireLockoutEvent($request); 64 | 65 | return $this->sendLockoutResponse($request); 66 | } 67 | 68 | $credentials = $this->getCredentials($request); 69 | 70 | if ($auth->attempt($credentials, $request->has('remember'))) { 71 | if ($auth->user()->confirmed != 1) { 72 | flash()->warning('This account is not confirmed, Please check your email for confirmation link. If you did not receive the confirmation code, please request for new confirmation code.'); 73 | $auth->logout(); 74 | 75 | return redirect('auth/login')->with('show_reconfirm', true); 76 | } 77 | 78 | return $this->handleUserWasAuthenticated($request, $throttles); 79 | } 80 | 81 | if ($throttles && !$lockedOut) { 82 | $this->incrementLoginAttempts($request); 83 | } 84 | 85 | return $this->sendFailedLoginResponse($request); 86 | } 87 | 88 | /** 89 | * Handle a registration request for the application. 90 | * 91 | * @param \Illuminate\Http\Request $request 92 | * 93 | * @return \Illuminate\Http\Response 94 | */ 95 | public function register(Request $request) 96 | { 97 | $validator = $this->validator($request->all()); 98 | 99 | if ($validator->fails()) { 100 | $this->throwValidationException( 101 | $request, $validator 102 | ); 103 | } 104 | 105 | // Check for duplicate user 106 | if ($this->checkForDupeUser()) { 107 | flash()->error('Your attempt to create duplicate account has been logged. If you believe this is an error please use IRC Chat!'); 108 | 109 | return redirect()->back()->withInput($request->all()); 110 | } 111 | 112 | // validate Email Address 113 | $email_verify = $this->verifyEmail($request->get('email')); 114 | if ($email_verify != 'OK') { 115 | return redirect()->back()->withInput($request->all())->withErrors([ 116 | 'email' => 'Rejected! ' . $email_verify . '. Please use different email address.', 117 | ]); 118 | } 119 | 120 | // Verify Recaptcha 121 | $recaptcha = new ReCaptcha(env('API_GOOGLE_RECAPTCHA')); 122 | $resp = $recaptcha->verify($request->get('g-recaptcha-response'), $request->getClientIp()); 123 | if (!$resp->isSuccess()) { 124 | flash()->error('ReCaptcha verification failed!'); 125 | 126 | return redirect()->back()->withInput($request->all()); 127 | } 128 | 129 | $user = $this->create($request->all()); 130 | if (!empty($user->id)) { 131 | return $this->sendRegistrationEmail($user); 132 | } 133 | 134 | flash()->error('Failed to Register! Please try again.'); 135 | 136 | return redirect()->back(); 137 | } 138 | 139 | /** 140 | * Get the needed authorization credentials from the request. 141 | * 142 | * @param \Illuminate\Http\Request $request 143 | * 144 | * @return array 145 | */ 146 | protected function getCredentials(Request $request) 147 | { 148 | $is_email = filter_var($request->get($this->loginUsername()), FILTER_VALIDATE_EMAIL); 149 | 150 | return [ 151 | $is_email ? 'email' : 'username' => $request->get($this->loginUsername()), 152 | 'password' => $request->get('password'), 153 | ]; 154 | } 155 | 156 | public function getForgot() 157 | { 158 | $this->meta->setMeta('Forgot Password'); 159 | 160 | return view('auth.forgot', $this->data); 161 | } 162 | 163 | public function postForgot(Request $request) 164 | { 165 | $this->validate($request, [ 166 | 'email_username' => 'required', 167 | ]); 168 | 169 | if (filter_var($request->get($this->loginUsername()), FILTER_VALIDATE_EMAIL)) { 170 | $user = User::where('email', $request->get($this->loginUsername()))->first(); 171 | } else { 172 | $user = User::where('username', $request->get($this->loginUsername()))->first(); 173 | } 174 | 175 | if ($user) { 176 | if ($user->forgot_at > carbon()->subMinutes(11)) { 177 | flash()->error('Forgot password already requested. Please wait for at least 10 minutes before requesting for new password reset!'); 178 | 179 | return redirect('auth/login'); 180 | } 181 | 182 | $user->forgot = 1; 183 | $user->forgot_at = carbon(); 184 | $user->forgot_token = md5($user->username . microtime() . $user->email . env('APP_KEY')); 185 | $user->save(); 186 | 187 | $this->dispatch(new SendForgotPasswordMail($user)); 188 | 189 | flash()->success("We've sent an email containing a temporary link that will allow you to reset your password for the next 24 hours. Please check your spam folder if the email doesn't appear within a few minutes."); 190 | 191 | return redirect('auth/login'); 192 | } 193 | 194 | flash()->error('Invalid Username or Email Address, no user found'); 195 | 196 | return redirect('auth/forgot'); 197 | } 198 | 199 | public function confirm($user_id, $token) 200 | { 201 | $user = User::where('id', '=', (int)$user_id) 202 | ->where('confirm_token', '=', $token) 203 | ->first(); 204 | if ($user) { 205 | $user->confirmed = 1; 206 | $user->confirm_token = null; 207 | $user->save(); 208 | 209 | flash()->success('Account confirmed successfully. You may login now.'); 210 | 211 | return redirect('auth/login'); 212 | } 213 | 214 | flash()->error('Invalid confirmation code or Account already confirmed!'); 215 | 216 | return redirect('auth/login')->with('show_reconfirm', true); 217 | } 218 | 219 | public function getReconfirm() 220 | { 221 | $this->meta->setMeta('Request New Confirmation Code'); 222 | 223 | return view('auth.reconfirm', $this->data); 224 | } 225 | 226 | public function postReconfirm(Request $request) 227 | { 228 | $rules = [ 229 | 'username' => 'required', 230 | 'password' => 'required', 231 | ]; 232 | if (!$request->has('old_email')) { 233 | $rules['email'] = 'required|email|unique:users'; 234 | } 235 | $this->validate($request, $rules); 236 | 237 | $credentials['username'] = $request->get('username'); 238 | $credentials['password'] = $request->get('password'); 239 | 240 | if (auth()->validate($credentials)) { 241 | $user = User::where('username', '=', $credentials['username'])->firstOrFail(); 242 | if ($user->confirmed == 1) { 243 | flash()->error('User already confirmed, Please login'); 244 | 245 | return redirect('auth/login'); 246 | } 247 | 248 | if ($user->updated_at > carbon()->subMinutes(6)) { 249 | flash()->error('Account Confirmation already requested. Please wait for at least 5 minutes before requesting for new confirmation code!'); 250 | 251 | return redirect()->back()->withInput($request->all()); 252 | } 253 | 254 | if (!$request->has('old_email')) { 255 | $user->email = $request->get('email'); 256 | } 257 | $user->confirm_token = $this->createActiveToken($user->toArray()); 258 | $user->confirmed = 0; 259 | $user->save(); 260 | 261 | return $this->sendRegistrationEmail($user); 262 | } 263 | 264 | flash()->error('Invalid Username or Password'); 265 | 266 | return redirect()->back()->withInput($request->all()); 267 | } 268 | 269 | public function getReset($user_id, $token) 270 | { 271 | $this->meta->setMeta('Reset Password'); 272 | $this->data['id'] = $user_id; 273 | $this->data['forgot_token'] = $token; 274 | 275 | $user = User::where('id', (int)$user_id)->where('forgot_token', $token)->where('forgot', 1)->first(); 276 | if ($user) { 277 | $this->data['user'] = $user; 278 | 279 | return view('auth.reset', $this->data); 280 | } 281 | 282 | flash()->error('Invalid password reset code!'); 283 | 284 | return redirect('auth/login'); 285 | } 286 | 287 | public function postReset(Request $request, $id, $forgot_token) 288 | { 289 | $this->validate($request, [ 290 | 'password' => 'required|confirmed|min:6', 291 | ]); 292 | 293 | $user = User::where('id', (int)$id)->where('forgot_token', $forgot_token)->where('forgot', 1)->first(); 294 | if ($user) { 295 | $user->password = bcrypt($request->get('password')); 296 | if ($user->confirmed != 1) { 297 | $user->confirmed = 1; 298 | $user->confirm_token = null; 299 | } 300 | 301 | $user->forgot = null; 302 | $user->forgot_token = null; 303 | $user->save(); 304 | 305 | flash()->success('Your password has been successfully reset. Please login with your new password.'); 306 | 307 | return redirect('auth/login'); 308 | } 309 | 310 | flash()->error('Invalid password reset code!'); 311 | 312 | return redirect('auth/login'); 313 | } 314 | 315 | /** 316 | * Get a validator for an incoming registration request. 317 | * 318 | * @param array $data 319 | * 320 | * @return \Illuminate\Contracts\Validation\Validator 321 | */ 322 | protected function validator(array $data) 323 | { 324 | return Validator::make($data, [ 325 | 'username' => 'required|alpha_dash|min:3|max:20|Unique:users', 326 | 'email' => 'required|email|max:255|unique:users', 327 | 'password' => 'required|confirmed|min:6', 328 | 'g-recaptcha-response' => 'required', 329 | ]); 330 | } 331 | 332 | /** 333 | * Create a new user instance after a valid registration. 334 | * 335 | * @param array $data 336 | * 337 | * @return User 338 | */ 339 | protected function create(array $data) 340 | { 341 | return User::create([ 342 | 'group_id' => $this->group_id, 343 | 'username' => $data['username'], 344 | 'email' => $data['email'], 345 | 'password' => bcrypt($data['password']), 346 | 'pid' => md5($data['username'] . $data['email'] . microtime() . $data['password'] . env('APP_KEY')), 347 | 'ip_address' => get_ip(), 348 | 'country_code' => geoip_country_code(get_ip()), 349 | 'show_adult' => true, 350 | 'can_download' => true, 351 | 'can_upload' => true, 352 | 'confirm_token' => $this->createActiveToken($data), 353 | 'confirmed' => false, 354 | ]); 355 | } 356 | 357 | /** 358 | * Create an unique Email activation/confirmation token. 359 | * 360 | * @param array $data 361 | * 362 | * @return string 363 | */ 364 | protected function createActiveToken(array $data) 365 | { 366 | return md5($data['username'] . $data['email'] . microtime() . env('APP_KEY')); 367 | } 368 | 369 | private function checkForDupeUser() 370 | { 371 | if (config('app.debug') && app()->environment() == 'local') { 372 | return false; 373 | } 374 | 375 | if (Cookie::has(env('APP_PREFIX') . 'love')) { 376 | return true; 377 | } 378 | 379 | $dupe_ips = User::where('ip_address', get_ip())->count(); 380 | if ($dupe_ips > 1 && $dupe_ips < 11) { 381 | return true; 382 | } 383 | 384 | $dupe_ips = Tracker::where('ip', get_ip())->groupBy('created_by')->count(); 385 | if ($dupe_ips > 1 && $dupe_ips < 11) { 386 | return true; 387 | } 388 | 389 | return false; 390 | } 391 | 392 | private function verifyEmail($email) 393 | { 394 | return app(EmailDomainVerifier::class)->verify($email); 395 | } 396 | 397 | private function sendRegistrationEmail($user) 398 | { 399 | $this->dispatch(new SendRegistrationMail($user)); 400 | 401 | flash()->info('You have been successfully registered. A confirmation email has been sent to "' . e($user->email) . '" Please confirm your email address, before you login.'); 402 | 403 | return redirect('auth/login')->with('show_reconfirm', true); 404 | } 405 | } 406 | -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | if("undefined"==typeof jQuery){throw new Error("Bootstrap's JavaScript requires jQuery")}+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2){throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}}(jQuery),+function(a){function b(){var c=document.createElement("bootstrap"),d={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var f in d){if(void 0!==c.style[f]){return{end:d[f]}}}return !1}a.fn.emulateTransitionEnd=function(d){var g=!1,f=this;a(this).one("bsTransitionEnd",function(){g=!0});var c=function(){g||a(f).trigger(a.support.transition.end)};return setTimeout(c,d),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(c){return a(c.target).is(this)?c.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(b){function c(g){return this.each(function(){var h=b(this),e=h.data("bs.alert");e||h.data("bs.alert",e=new d(this)),"string"==typeof g&&e[g].call(h)})}var f='[data-dismiss="alert"]',d=function(g){b(g).on("click",f,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(k){function l(){g.detach().trigger("closed.bs.alert").remove()}var h=b(this),j=h.attr("data-target");j||(j=h.attr("href"),j=j&&j.replace(/.*(?=#[^\s]*$)/,""));var g=b(j);k&&k.preventDefault(),g.length||(g=h.closest(".alert")),g.trigger(k=b.Event("close.bs.alert")),k.isDefaultPrevented()||(g.removeClass("in"),b.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",l).emulateTransitionEnd(d.TRANSITION_DURATION):l())};var a=b.fn.alert;b.fn.alert=c,b.fn.alert.Constructor=d,b.fn.alert.noConflict=function(){return b.fn.alert=a,this},b(document).on("click.bs.alert.data-api",f,d.prototype.close)}(jQuery),+function(a){function b(f){return this.each(function(){var h=a(this),e=h.data("bs.button"),g="object"==typeof f&&f;e||h.data("bs.button",e=new d(this,g)),"toggle"==f?e.toggle():f&&e.setState(f)})}var d=function(f,g){this.$element=a(f),this.options=a.extend({},d.DEFAULTS,g),this.isLoading=!1};d.VERSION="3.3.6",d.DEFAULTS={loadingText:"loading..."},d.prototype.setState=function(h){var k="disabled",j=this.$element,f=j.is("input")?"val":"html",g=j.data();h+="Text",null==g.resetText&&j.data("resetText",j[f]()),setTimeout(a.proxy(function(){j[f](null==g[h]?this.options[h]:g[h]),"loadingText"==h?(this.isLoading=!0,j.addClass(k).attr(k,k)):this.isLoading&&(this.isLoading=!1,j.removeClass(k).removeAttr(k))},this),0)},d.prototype.toggle=function(){var f=!0,g=this.$element.closest('[data-toggle="buttons"]');if(g.length){var h=this.$element.find("input");"radio"==h.prop("type")?(h.prop("checked")&&(f=!1),g.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==h.prop("type")&&(h.prop("checked")!==this.$element.hasClass("active")&&(f=!1),this.$element.toggleClass("active")),h.prop("checked",this.$element.hasClass("active")),f&&h.trigger("change")}else{this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")}};var c=a.fn.button;a.fn.button=b,a.fn.button.Constructor=d,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(f){var e=a(f.target);e.hasClass("btn")||(e=e.closest(".btn")),b.call(e,"toggle"),a(f.target).is('input[type="radio"]')||a(f.target).is('input[type="checkbox"]')||f.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(f){a(f.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(f.type))})}(jQuery),+function(b){function c(g){return this.each(function(){var k=b(this),h=k.data("bs.carousel"),j=b.extend({},f.DEFAULTS,k.data(),"object"==typeof g&&g),e="string"==typeof g?g:j.slide;h||k.data("bs.carousel",h=new f(this,j)),"number"==typeof g?h.to(g):e?h[e]():j.interval&&h.pause().cycle()})}var f=function(g,h){this.$element=b(g),this.$indicators=this.$element.find(".carousel-indicators"),this.options=h,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",b.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart" in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",b.proxy(this.pause,this)).on("mouseleave.bs.carousel",b.proxy(this.cycle,this))};f.VERSION="3.3.6",f.TRANSITION_DURATION=600,f.DEFAULTS={interval:5000,pause:"hover",wrap:!0,keyboard:!0},f.prototype.keydown=function(e){if(!/input|textarea/i.test(e.target.tagName)){switch(e.which){case 37:this.prev();break;case 39:this.next();break;default:return}e.preventDefault()}},f.prototype.cycle=function(g){return g||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(b.proxy(this.next,this),this.options.interval)),this},f.prototype.getItemIndex=function(e){return this.$items=e.parent().children(".item"),this.$items.index(e||this.$active)},f.prototype.getItemForDirection=function(h,k){var m=this.getItemIndex(k),l="prev"==h&&0===m||"next"==h&&m==this.$items.length-1;if(l&&!this.options.wrap){return k}var g="prev"==h?-1:1,j=(m+g)%this.$items.length;return this.$items.eq(j)},f.prototype.to=function(g){var h=this,i=this.getItemIndex(this.$active=this.$element.find(".item.active"));return g>this.$items.length-1||0>g?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){h.to(g)}):i==g?this.pause().cycle():this.slide(g>i?"next":"prev",this.$items.eq(g))},f.prototype.pause=function(g){return g||(this.paused=!0),this.$element.find(".next, .prev").length&&b.support.transition&&(this.$element.trigger(b.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},f.prototype.next=function(){return this.sliding?void 0:this.slide("next")},f.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},f.prototype.slide=function(n,j){var m=this.$element.find(".item.active"),x=j||this.getItemForDirection(n,m),v=this.interval,g="next"==n?"left":"right",k=this;if(x.hasClass("active")){return this.sliding=!1}var t=x[0],q=b.Event("slide.bs.carousel",{relatedTarget:t,direction:g});if(this.$element.trigger(q),!q.isDefaultPrevented()){if(this.sliding=!0,v&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var w=b(this.$indicators.children()[this.getItemIndex(x)]);w&&w.addClass("active")}var h=b.Event("slid.bs.carousel",{relatedTarget:t,direction:g});return b.support.transition&&this.$element.hasClass("slide")?(x.addClass(n),x[0].offsetWidth,m.addClass(g),x.addClass(g),m.one("bsTransitionEnd",function(){x.removeClass([n,g].join(" ")).addClass("active"),m.removeClass(["active",g].join(" ")),k.sliding=!1,setTimeout(function(){k.$element.trigger(h)},0)}).emulateTransitionEnd(f.TRANSITION_DURATION)):(m.removeClass("active"),x.addClass("active"),this.sliding=!1,this.$element.trigger(h)),v&&this.cycle(),this}};var d=b.fn.carousel;b.fn.carousel=c,b.fn.carousel.Constructor=f,b.fn.carousel.noConflict=function(){return b.fn.carousel=d,this};var a=function(l){var k,g=b(this),h=b(g.attr("data-target")||(k=g.attr("href"))&&k.replace(/.*(?=#[^\s]+$)/,""));if(h.hasClass("carousel")){var e=b.extend({},h.data(),g.data()),j=g.attr("data-slide-to");j&&(e.interval=!1),c.call(h,e),j&&h.data("bs.carousel").to(j),l.preventDefault()}};b(document).on("click.bs.carousel.data-api","[data-slide]",a).on("click.bs.carousel.data-api","[data-slide-to]",a),b(window).on("load",function(){b('[data-ride="carousel"]').each(function(){var e=b(this);c.call(e,e.data())})})}(jQuery),+function(b){function c(g){var i,h=g.attr("data-target")||(i=g.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,"");return b(h)}function f(g){return this.each(function(){var j=b(this),e=j.data("bs.collapse"),h=b.extend({},d.DEFAULTS,j.data(),"object"==typeof g&&g);!e&&h.toggle&&/show|hide/.test(g)&&(h.toggle=!1),e||j.data("bs.collapse",e=new d(this,h)),"string"==typeof g&&e[g]()})}var d=function(g,h){this.$element=b(g),this.options=b.extend({},d.DEFAULTS,h),this.$trigger=b('[data-toggle="collapse"][href="#'+g.id+'"],[data-toggle="collapse"][data-target="#'+g.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var e=this.$element.hasClass("width");return e?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var n,j=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(j&&j.length&&(n=j.data("bs.collapse"),n&&n.transitioning))){var k=b.Event("show.bs.collapse");if(this.$element.trigger(k),!k.isDefaultPrevented()){j&&j.length&&(f.call(j,"hide"),n||j.data("bs.collapse",null));var h=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[h](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var m=function(){this.$element.removeClass("collapsing").addClass("collapse in")[h](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!b.support.transition){return m.call(this)}var g=b.camelCase(["scroll",h].join("-"));this.$element.one("bsTransitionEnd",b.proxy(m,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[h](this.$element[0][g])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var h=b.Event("hide.bs.collapse");if(this.$element.trigger(h),!h.isDefaultPrevented()){var j=this.dimension();this.$element[j](this.$element[j]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var g=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return b.support.transition?void this.$element[j](0).one("bsTransitionEnd",b.proxy(g,this)).emulateTransitionEnd(d.TRANSITION_DURATION):g.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return b(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(b.proxy(function(h,g){var e=b(g);this.addAriaAndCollapsedClass(c(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(g,h){var i=g.hasClass("in");g.attr("aria-expanded",i),h.toggleClass("collapsed",!i).attr("aria-expanded",i)};var a=b.fn.collapse;b.fn.collapse=f,b.fn.collapse.Constructor=d,b.fn.collapse.noConflict=function(){return b.fn.collapse=a,this},b(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(k){var g=b(this);g.attr("data-target")||k.preventDefault();var h=c(g),e=h.data("bs.collapse"),j=e?"toggle":g.data();f.call(h,j)})}(jQuery),+function(d){function h(a){var l=a.attr("data-target");l||(l=a.attr("href"),l=l&&/#[A-Za-z]/.test(l)&&l.replace(/.*(?=#[^\s]*$)/,""));var i=l&&d(l);return i&&i.length?i:a.parent()}function k(a){a&&3===a.which||(d(c).remove(),d(f).each(function(){var m=d(this),e=h(m),l={relatedTarget:this};e.hasClass("open")&&(a&&"click"==a.type&&/input|textarea/i.test(a.target.tagName)&&d.contains(e[0],a.target)||(e.trigger(a=d.Event("hide.bs.dropdown",l)),a.isDefaultPrevented()||(m.attr("aria-expanded","false"),e.removeClass("open").trigger(d.Event("hidden.bs.dropdown",l)))))}))}function j(a){return this.each(function(){var i=d(this),e=i.data("bs.dropdown");e||i.data("bs.dropdown",e=new b(this)),"string"==typeof a&&e[a].call(i)})}var c=".dropdown-backdrop",f='[data-toggle="dropdown"]',b=function(a){d(a).on("click.bs.dropdown",this.toggle)};b.VERSION="3.3.6",b.prototype.toggle=function(p){var l=d(this);if(!l.is(".disabled, :disabled")){var m=h(l),e=m.hasClass("open");if(k(),!e){"ontouchstart" in document.documentElement&&!m.closest(".navbar-nav").length&&d(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(d(this)).on("click",k);var n={relatedTarget:this};if(m.trigger(p=d.Event("show.bs.dropdown",n)),p.isDefaultPrevented()){return}l.trigger("focus").attr("aria-expanded","true"),m.toggleClass("open").trigger(d.Event("shown.bs.dropdown",n))}return !1}},b.prototype.keydown=function(u){if(/(38|40|27|32)/.test(u.which)&&!/input|textarea/i.test(u.target.tagName)){var s=d(this);if(u.preventDefault(),u.stopPropagation(),!s.is(".disabled, :disabled")){var p=h(s),m=p.hasClass("open");if(!m&&27!=u.which||m&&27==u.which){return 27==u.which&&p.find(f).trigger("focus"),s.trigger("click")}var q=" li:not(.disabled):visible a",e=p.find(".dropdown-menu"+q);if(e.length){var t=e.index(u.target);38==u.which&&t>0&&t--,40==u.which&&tdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&e?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!e?this.scrollbarWidth:""})},d.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},d.prototype.checkScrollbar=function(){var f=window.innerWidth;if(!f){var g=document.documentElement.getBoundingClientRect();f=g.right-Math.abs(g.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},d.prototype.init=function(m,q,p){if(this.enabled=!0,this.type=m,this.$element=a(q),this.options=this.getOptions(p),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0] instanceof document.constructor&&!this.options.selector){throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!")}for(var h=this.options.trigger.split(" "),j=h.length;j--;){var g=h[j];if("click"==g){this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this))}else{if("manual"!=g){var k="hover"==g?"mouseenter":"focusin",f="hover"==g?"mouseleave":"focusout";this.$element.on(k+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},d.prototype.getDefaults=function(){return d.DEFAULTS},d.prototype.getOptions=function(f){return f=a.extend({},this.getDefaults(),this.$element.data(),f),f.delay&&"number"==typeof f.delay&&(f.delay={show:f.delay,hide:f.delay}),f},d.prototype.getDelegateOptions=function(){var f={},g=this.getDefaults();return this._options&&a.each(this._options,function(e,h){g[e]!=h&&(f[e]=h)}),f},d.prototype.enter=function(f){var g=f instanceof this.constructor?f:a(f.currentTarget).data("bs."+this.type);return g||(g=new this.constructor(f.currentTarget,this.getDelegateOptions()),a(f.currentTarget).data("bs."+this.type,g)),f instanceof a.Event&&(g.inState["focusin"==f.type?"focus":"hover"]=!0),g.tip().hasClass("in")||"in"==g.hoverState?void (g.hoverState="in"):(clearTimeout(g.timeout),g.hoverState="in",g.options.delay&&g.options.delay.show?void (g.timeout=setTimeout(function(){"in"==g.hoverState&&g.show()},g.options.delay.show)):g.show())},d.prototype.isInStateTrue=function(){for(var e in this.inState){if(this.inState[e]){return !0}}return !1},d.prototype.leave=function(f){var g=f instanceof this.constructor?f:a(f.currentTarget).data("bs."+this.type);return g||(g=new this.constructor(f.currentTarget,this.getDelegateOptions()),a(f.currentTarget).data("bs."+this.type,g)),f instanceof a.Event&&(g.inState["focusout"==f.type?"focus":"hover"]=!1),g.isInStateTrue()?void 0:(clearTimeout(g.timeout),g.hoverState="out",g.options.delay&&g.options.delay.hide?void (g.timeout=setTimeout(function(){"out"==g.hoverState&&g.hide()},g.options.delay.hide)):g.hide())},d.prototype.show=function(){var y=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(y);var k=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(y.isDefaultPrevented()||!k){return}var t=this,E=this.tip(),B=this.getUID(this.type);this.setContent(),E.attr("id",B),this.$element.attr("aria-describedby",B),this.options.animation&&E.addClass("fade");var g="function"==typeof this.options.placement?this.options.placement.call(this,E[0],this.$element[0]):this.options.placement,q=/\s?auto?\s?/i,A=q.test(g);A&&(g=g.replace(q,"")||"top"),E.detach().css({top:0,left:0,display:"block"}).addClass(g).data("bs."+this.type,this),this.options.container?E.appendTo(this.options.container):E.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var z=this.getPosition(),D=E[0].offsetWidth,j=E[0].offsetHeight;if(A){var w=g,x=this.getPosition(this.$viewport);g="bottom"==g&&z.bottom+j>x.bottom?"top":"top"==g&&z.top-jx.width?"left":"left"==g&&z.left-Du.top+u.height&&(k.top=u.top+u.height-j)}else{var q=m.left-w,p=m.left+w+h;qu.right&&(k.left=u.left+u.width-p)}return k},d.prototype.getTitle=function(){var f,g=this.$element,h=this.options;return f=g.attr("data-original-title")||("function"==typeof h.title?h.title.call(g[0]):h.title)},d.prototype.getUID=function(e){do{e+=~~(1000000*Math.random())}while(document.getElementById(e));return e},d.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length)){throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!")}return this.$tip},d.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},d.prototype.enable=function(){this.enabled=!0},d.prototype.disable=function(){this.enabled=!1},d.prototype.toggleEnabled=function(){this.enabled=!this.enabled},d.prototype.toggle=function(f){var g=this;f&&(g=a(f.currentTarget).data("bs."+this.type),g||(g=new this.constructor(f.currentTarget,this.getDelegateOptions()),a(f.currentTarget).data("bs."+this.type,g))),f?(g.inState.click=!g.inState.click,g.isInStateTrue()?g.enter(g):g.leave(g)):g.tip().hasClass("in")?g.leave(g):g.enter(g)},d.prototype.destroy=function(){var e=this;clearTimeout(this.timeout),this.hide(function(){e.$element.off("."+e.type).removeData("bs."+e.type),e.$tip&&e.$tip.detach(),e.$tip=null,e.$arrow=null,e.$viewport=null})};var c=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=d,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(jQuery),+function(a){function b(f){return this.each(function(){var h=a(this),e=h.data("bs.popover"),g="object"==typeof f&&f;(e||!/destroy|hide/.test(f))&&(e||h.data("bs.popover",e=new d(this,g)),"string"==typeof f&&e[f]())})}var d=function(f,g){this.init("popover",f,g)};if(!a.fn.tooltip){throw new Error("Popover requires tooltip.js")}d.VERSION="3.3.6",d.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),d.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),d.prototype.constructor=d,d.prototype.getDefaults=function(){return d.DEFAULTS},d.prototype.setContent=function(){var f=this.tip(),g=this.getTitle(),h=this.getContent();f.find(".popover-title")[this.options.html?"html":"text"](g),f.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof h?"html":"append":"text"](h),f.removeClass("fade top bottom left right in"),f.find(".popover-title").html()||f.find(".popover-title").hide()},d.prototype.hasContent=function(){return this.getTitle()||this.getContent()},d.prototype.getContent=function(){var f=this.$element,g=this.options;return f.attr("data-content")||("function"==typeof g.content?g.content.call(f[0]):g.content)},d.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var c=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=d,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){function b(f,e){this.$body=a(document.body),this.$scrollElement=a(a(f).is(document.body)?window:f),this.options=a.extend({},b.DEFAULTS,e),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function d(e){return this.each(function(){var h=a(this),f=h.data("bs.scrollspy"),g="object"==typeof e&&e;f||h.data("bs.scrollspy",f=new b(this,g)),"string"==typeof e&&f[e]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var f=this,h="offset",g=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(h="position",g=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var l=a(this),j=l.data("target")||l.attr("href"),k=/^#./.test(j)&&a(j);return k&&k.length&&k.is(":visible")&&[[k[h]().top+g,j]]||null}).sort(function(i,j){return i[0]-j[0]}).each(function(){f.offsets.push(this[0]),f.targets.push(this[1])})},b.prototype.process=function(){var h,k=this.$scrollElement.scrollTop()+this.options.offset,m=this.getScrollHeight(),l=this.options.offset+m-this.$scrollElement.height(),g=this.offsets,j=this.targets,f=this.activeTarget;if(this.scrollHeight!=m&&this.refresh(),k>=l){return f!=(h=j[j.length-1])&&this.activate(h)}if(f&&k=g[h]&&(void 0===g[h+1]||k .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),l.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),k?(l[0].offsetWidth,l.addClass("in")):l.removeClass("fade"),l.parent(".dropdown-menu").length&&l.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),h&&h()}var g=m.find("> .active"),k=h&&b.support.transition&&(g.length&&g.hasClass("fade")||!!m.find("> .fade").length);g.length&&k?g.one("bsTransitionEnd",j).emulateTransitionEnd(f.TRANSITION_DURATION):j(),g.removeClass("in")};var d=b.fn.tab;b.fn.tab=c,b.fn.tab.Constructor=f,b.fn.tab.noConflict=function(){return b.fn.tab=d,this};var a=function(e){e.preventDefault(),c.call(b(this),"show")};b(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',a).on("click.bs.tab.data-api",'[data-toggle="pill"]',a)}(jQuery),+function(a){function b(f){return this.each(function(){var h=a(this),e=h.data("bs.affix"),g="object"==typeof f&&f;e||h.data("bs.affix",e=new d(this,g)),"string"==typeof f&&e[f]()})}var d=function(f,g){this.options=a.extend({},d.DEFAULTS,g),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(f),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};d.VERSION="3.3.6",d.RESET="affix affix-top affix-bottom",d.DEFAULTS={offset:0,target:window},d.prototype.getState=function(u,m,h,g){var k=this.$target.scrollTop(),v=this.$element.offset(),q=this.$target.height();if(null!=h&&"top"==this.affixed){return h>k?"top":!1}if("bottom"==this.affixed){return null!=h?k+this.unpin<=v.top?!1:"bottom":u-g>=k+q?!1:"bottom"}var f=null==this.affixed,j=f?k:v.top,p=f?q:m;return null!=h&&h>=k?"top":null!=g&&j+p>=u-g?"bottom":!1},d.prototype.getPinnedOffset=function(){if(this.pinnedOffset){return this.pinnedOffset}this.$element.removeClass(d.RESET).addClass("affix");var f=this.$target.scrollTop(),g=this.$element.offset();return this.pinnedOffset=g.top-f},d.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},d.prototype.checkPosition=function(){if(this.$element.is(":visible")){var m=this.$element.height(),n=this.options.offset,h=n.top,j=n.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof n&&(j=h=n),"function"==typeof h&&(h=n.top(this.$element)),"function"==typeof j&&(j=n.bottom(this.$element));var k=this.getState(g,m,h,j);if(this.affixed!=k){null!=this.unpin&&this.$element.css("top","");var f="affix"+(k?"-"+k:""),p=a.Event(f+".bs.affix");if(this.$element.trigger(p),p.isDefaultPrevented()){return}this.affixed=k,this.unpin="bottom"==k?this.getPinnedOffset():null,this.$element.removeClass(d.RESET).addClass(f).trigger(f.replace("affix","affixed")+".bs.affix")}"bottom"==k&&this.$element.offset({top:g-m-j})}};var c=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=d,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var f=a(this),e=f.data();e.offset=e.offset||{},null!=e.offsetBottom&&(e.offset.bottom=e.offsetBottom),null!=e.offsetTop&&(e.offset.top=e.offsetTop),b.call(f,e)})})}(jQuery),!function(a,b,d){!function c(h,k,l){function g(i,m){if(!k[i]){if(!h[i]){var e="function"==typeof require&&require;if(!m&&e){return e(i,!0)}if(j){return j(i,!0)}var o=new Error("Cannot find module '"+i+"'");throw o.code="MODULE_NOT_FOUND",o}var n=k[i]={exports:{}};h[i][0].call(n.exports,function(p){var q=h[i][1][p];return g(q?q:p)},n,n.exports,c,h,k,l)}return k[i].exports}for(var j="function"==typeof require&&require,f=0;f=0;){h=h.replace(" "+g+" "," ")}f.className=h.replace(/^\s+|\s+$/g,"")}},w=function(f){var g=b.createElement("div");return g.appendChild(b.createTextNode(f)),g.innerHTML},D=function(f){f.style.opacity="",f.style.display="block"},C=function(f){if(f&&!f.length){return D(f)}for(var g=0;g0?setTimeout(h,g):f.style.display="none"});h()},A=function(h){if("function"==typeof MouseEvent){var g=new MouseEvent("click",{view:a,bubbles:!1,cancelable:!0});h.dispatchEvent(g)}else{if(b.createEvent){var f=b.createEvent("MouseEvents");f.initEvent("click",!1,!1),h.dispatchEvent(f)}else{b.createEventObject?h.fireEvent("onclick"):"function"==typeof h.onclick&&h.onclick()}}},E=function(f){"function"==typeof f.stopPropagation?(f.stopPropagation(),f.preventDefault()):a.event&&a.event.hasOwnProperty("cancelBubble")&&(a.event.cancelBubble=!0)};x.hasClass=I,x.addClass=F,x.removeClass=e,x.escapeHtml=w,x._show=D,x.show=C,x._hide=H,x.hide=j,x.isDescendant=z,x.getTopMargin=B,x.fadeIn=t,x.fadeOut=G,x.fireClick=A,x.stopEventPropagation=E},{}],5:[function(k,l,g){Object.defineProperty(g,"__esModule",{value:!0});var h=k("./handle-dom"),f=k("./handle-swal-dom"),j=function(x,q,t){var m=x||a.event,s=m.keyCode||m.which,z=t.querySelector("button.confirm"),y=t.querySelector("button.cancel"),A=t.querySelectorAll("button[tabindex]");if(-1!==[9,13,32,27].indexOf(s)){for(var n=m.target||m.srcElement,v=-1,w=0;w"),s.innerHTML=H.html?H.text:g.escapeHtml(H.text||"").split("\n").join("
"),H.text&&g.show(s),H.customClass){g.addClass(A,H.customClass),A.setAttribute("data-custom-class",H.customClass)}else{var G=A.getAttribute("data-custom-class");g.removeClass(A,G),A.setAttribute("data-custom-class","")}if(g.hide(A.querySelectorAll(".sa-icon")),H.type&&!h.isIE8()){var i=function(){for(var v=!1,t=0;tu;u++){v=parseInt(r.substr(2*u,2),16),v=Math.round(Math.min(Math.max(0,v+v*s),255)).toString(16),l+=("00"+v).substr(v.length)}return l};p.extend=h,p.hexToRgb=j,p.isIE8=g,p.logStr=k,p.colorLuminance=f},{}]},{},[1]),"function"==typeof define&&define.amd?define(function(){return sweetAlert}):"undefined"!=typeof module&&module.exports&&(module.exports=sweetAlert)}(window,document);var BASEURL=$("meta[name=_base_url]").attr("content"),shortenUrl=function(){$('input[name="short_url"]').on("click",function(){$(this).select()}),$("#form_shortener").on("submit",function(a){var b=$(this);b.find(".form-error").remove(),b.find(":submit").addClass("disabled").attr("disabled","disabled"),b.find(".shorten-output").append(''),$.ajax({url:b.attr("action"),type:"POST",data:b.serializeArray(),dataType:"json"}).done(function(c){b.find(".input-group").removeClass("has-error"),c&&c.url&&(b.find(".short-url-group").removeClass("hidden"),b.find('input[name="short_url"]').val(c.url))}).fail(function(c){b.find(".short-url-group").addClass("hidden"),b.find(".input-group").addClass("has-error"),"string"==$.type(c.responseJSON)?b.find(".shorten-output").append(''+c.responseJSON+""):"object"==$.type(c.responseJSON)?$.each(c.responseJSON,function(d,e){0!=e.length&&b.find(".shorten-output").append(''+e+"")}):b.find(".shorten-output").append(''+c.statusText+"")}).always(function(){b.find(".save-spinner").remove(),b.find(":submit").removeClass("disabled").removeAttr("disabled")}),a.preventDefault()}),$.ajax({url:BASEURL+"/csrf",type:"GET"}).done(function(a){$('input[name="_token"]').val(a)})}; --------------------------------------------------------------------------------