├── .env.example ├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── app ├── Console │ ├── Commands │ │ └── Inspire.php │ └── Kernel.php ├── Events │ └── Event.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── Account │ │ │ ├── AccountController.php │ │ │ └── PreferencesController.php │ │ ├── Admin │ │ │ └── AdminController.php │ │ ├── Api │ │ │ └── UploadController.php │ │ ├── Auth │ │ │ ├── AuthController.php │ │ │ └── PasswordController.php │ │ ├── Controller.php │ │ ├── DownloadController.php │ │ └── IndexController.php │ ├── Kernel.php │ ├── Middleware │ │ ├── AdminAuthenticate.php │ │ ├── ApiAuthenticate.php │ │ ├── Authenticate.php │ │ ├── EncryptCookies.php │ │ ├── RedirectIfAuthenticated.php │ │ └── VerifyCsrfToken.php │ ├── Requests │ │ └── Request.php │ ├── helpers.php │ └── routes.php ├── Jobs │ └── Job.php ├── Listeners │ └── .gitkeep ├── Models │ ├── Upload.php │ ├── User.php │ └── UserPreferences.php └── Providers │ ├── AppServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php ├── artisan ├── bootstrap ├── app.php ├── autoload.php └── cache │ └── .gitignore ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── compile.php ├── database.php ├── debugbar.php ├── filesystems.php ├── ide-helper.php ├── image.php ├── mail.php ├── queue.php ├── recaptcha.php ├── services.php ├── session.php ├── upste.php └── view.php ├── database ├── .gitignore ├── factories │ └── ModelFactory.php ├── migrations │ ├── .gitkeep │ ├── 2015_12_09_172957_create_users_table.php │ ├── 2015_12_09_173005_create_uploads_table.php │ ├── 2015_12_09_173022_create_password_resets_table.php │ ├── 2015_12_09_235053_fix_hash_length_for_uploads_table.php │ ├── 2015_12_11_221322_create_sessions_table.php │ ├── 2015_12_15_101257_create_jobs_table.php │ ├── 2015_12_15_101552_create_failed_jobs_table.php │ ├── 2016_02_20_173427_update_session_drivers.php │ ├── 2016_03_20_131515_add_original_hash.php │ ├── 2016_03_30_213843_increase_password_length.php │ ├── 2016_04_01_022122_add_view_counter.php │ ├── 2016_04_01_025729_rename_download.php │ ├── 2016_04_03_195138_create_preferences_table.php │ ├── 2016_04_03_213046_update_default_pagination.php │ └── 2016_07_26_153539_add_email_confirmation.php └── seeds │ ├── .gitkeep │ └── DatabaseSeeder.php ├── gulpfile.js ├── nginx.conf.example ├── package.json ├── phpspec.yml ├── phpunit.xml ├── public ├── .gitignore ├── .htaccess ├── assets │ ├── css │ │ └── .gitignore │ ├── img │ │ ├── favicon.png │ │ └── thumbnail.png │ └── js │ │ └── .gitignore ├── favicon.ico ├── index.php ├── robots.txt └── vendor │ └── folklore │ └── image │ ├── .gitkeep │ └── js │ └── image.js ├── readme.md ├── resources ├── assets │ ├── js │ │ ├── dropzone.js │ │ ├── global.js │ │ └── thumbnailhover.js │ └── sass │ │ ├── .gitignore │ │ ├── error.scss │ │ ├── global.scss │ │ └── thumbnailhover.scss ├── lang │ └── en │ │ ├── messages.php │ │ ├── pagination.php │ │ ├── passwords.php │ │ └── validation.php └── views │ ├── account │ ├── api.blade.php │ ├── faq.blade.php │ ├── index.blade.php │ ├── preferences.blade.php │ ├── resources.blade.php │ ├── resources │ │ └── bash.blade.php │ └── uploads.blade.php │ ├── admin │ ├── index.blade.php │ ├── requests.blade.php │ ├── uploads.blade.php │ └── users.blade.php │ ├── auth │ ├── login.blade.php │ ├── password.blade.php │ ├── register.blade.php │ └── reset.blade.php │ ├── emails │ ├── admin │ │ ├── exception.blade.php │ │ └── new_registration.blade.php │ └── user │ │ ├── account_accepted.blade.php │ │ ├── account_confirmation.blade.php │ │ ├── account_rejected.blade.php │ │ ├── api_key_reset.blade.php │ │ └── password_reset.blade.php │ ├── errors │ ├── 404.blade.php │ └── 503.blade.php │ ├── index.blade.php │ ├── layouts │ ├── account.blade.php │ ├── admin.blade.php │ └── master.blade.php │ └── vendor │ ├── .gitkeep │ └── flash │ └── message.blade.php ├── server.php ├── storage ├── .gitignore ├── app │ ├── .gitignore │ ├── thumbnails │ │ └── .gitignore │ └── uploads │ │ └── .gitignore ├── debugbar │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore └── tests ├── ExampleTest.php └── TestCase.php /.env.example: -------------------------------------------------------------------------------- 1 | APP_ENV=local 2 | ## Make sure this is disabled for production or it will leak sensitive data. 3 | APP_DEBUG=false 4 | APP_KEY=SomeRandomString 5 | 6 | DB_CONNECTION=sqlite 7 | DB_HOST=localhost 8 | DB_DATABASE=homestead 9 | DB_USERNAME=homestead 10 | DB_PASSWORD=secret 11 | 12 | CACHE_DRIVER=file 13 | SESSION_DRIVER=file 14 | QUEUE_DRIVER=sync 15 | 16 | MAIL_DRIVER=smtp 17 | MAIL_HOST=mailtrap.io 18 | MAIL_PORT=2525 19 | MAIL_USERNAME=null 20 | MAIL_PASSWORD=null 21 | MAIL_ENCRYPTION=null 22 | MAIL_FROM=noreply@example.com 23 | 24 | ## Should cookies only be sent over a HTTPS connection? 25 | SECURE_COOKIES=false 26 | 27 | ## Which domains cookies are valid for. Should be set to .whateveryourdomainis.com 28 | COOKIE_DOMAIN=.example.com 29 | 30 | ## Bcrypt work factor. 10 is a good setting for most systems. 31 | PASSWORD_HASH_ROUNDS=10 32 | 33 | ## The name that this site will be referred to in emails and certain UI elements. 34 | SITE_NAME=uPste 35 | 36 | ## The url files are served from. 37 | UPLOAD_URL=https://a.example.com 38 | 39 | ## The initial length of upload slugs. 40 | ## This will increase as slugs become unavailable. 41 | UPLOAD_SLUG_LENGTH=3 42 | 43 | ## Sets the method for sending files to users. 44 | ## It is recommended to use one of 'x-accel' (nginx) or 'x-sendfile' (apache) 45 | ## See the included nginx.conf.example's /upload/ location for a working implementation. 46 | ## PRs are welcome for Apache implementations. 47 | ## Set to null to just use Laravel's Response::download() method. 48 | SENDFILE_METHOD=null 49 | 50 | ## Your email address. Used to send registration alerts and uncaught exceptions. 51 | OWNER_EMAIL=me@example.com 52 | 53 | ## Your name. 54 | OWNER_NAME=Me 55 | 56 | ## A link to public GPG key. Used to inform users how to securely contact you. 57 | OWNER_GPG=null 58 | 59 | ## Upload limit per file IN MEGABYTES. 60 | ## Please note that your web server AND php.ini have to support AT LEAST this amount. 61 | PER_UPLOAD_LIMIT=20 62 | 63 | ## Maximum amount of disk space a user can use IN MEGABYTES. Set to 0 for unlimited. 64 | USER_STORAGE_QUOTA=0 65 | 66 | ## Require an admin to manually activate user accounts before they can be used. 67 | REQUIRE_USER_APPROVAL=true 68 | 69 | ## Require users to verify their email via a link sent to their email address before the registration/request is processed. 70 | REQUIRE_EMAIL_VERIFICATION=false 71 | 72 | ## An IRC channel and server your users can contact you in. 73 | IRC_CHANNEL=null 74 | IRC_SERVER=null 75 | 76 | ## Attempt to remove EXIF tags from png and jpg images. 77 | ## This helps prevent accidental leaks of sensitive information like geolocation, 78 | ## but also modifies the uploaded files which may be undesirable. 79 | STRIP_EXIF=true 80 | 81 | ## Google recaptcha keys for registration. 82 | ## https://www.google.com/recaptcha 83 | ## Set either to null to disable recaptcha. 84 | RECAPTCHA_PUBLIC_KEY=null 85 | RECAPTCHA_PRIVATE_KEY=null 86 | 87 | ## None of these settings will be used unless you're using the redis driver for session, queue, or cache 88 | ## Redis connection scheme. Most people will be using either 'tcp' or 'unix' 89 | REDIS_SCHEME=tcp 90 | ## Ignored when REDIS_SCHEME is 'unix' 91 | REDIS_HOST=127.0.0.1 92 | REDIS_PORT=6379 93 | REDIS_PASSWORD=null 94 | ## Only used when REDIS_SCHEME is 'unix' 95 | REDIS_PATH=/tmp/redis.sock 96 | REDIS_DATABASE=0 97 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.less linguist-vendored 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /node_modules 3 | Homestead.yaml 4 | .env 5 | _ide_helper.php 6 | .phpstorm.meta.php 7 | _ide_helper_models.php 8 | public/.well-known 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Please view this file on the master branch, on stable branches it's out of date. 2 | 3 | v 1.2.3 4 | - [ENHANCEMENT] Add better file extension detection. 5 | 6 | v 1.2.2 7 | - [FIX] Account for email confirmation in user SELECTs. 8 | 9 | v 1.2.1 10 | - [FIX] Fix API error when no session is available. 11 | 12 | v 1.2.0 13 | - [FEATURE] Add option email confirmation on register. 14 | 15 | v 1.1.6 16 | - [FIX] Prevent an edge case with view caching involving two users having the same file hash. 17 | 18 | v 1.1.5 19 | - Make view cache file-specific. 20 | 21 | v 1.1.4 22 | - Cache client IPs on file view so views can't be spammed. 23 | 24 | v 1.1.3 25 | - Fix username apostrophe usage. 26 | 27 | v 1.1.2 28 | - Totally refactor caching so models pretty much handle their own caching with no outside interference. 29 | 30 | v 1.1.1 31 | - Clean up email subjects and fix routes when using daemon queue worker. 32 | 33 | v 1.1.0 34 | - Add deletion URLs for sharex compatibility. 35 | 36 | v 1.0.10 37 | - Fixed all the bugs in image previews. 38 | 39 | v 1.0.9 40 | - Add image previews on thumbnail hover. 41 | 42 | v 1.0.8 43 | - Switch to fit() for image resizing as it retains aspect ratio. 44 | 45 | v 1.0.7 46 | - Add bcrypt work factor to env 47 | - Add Honeypot service to further reduce spambots registering. 48 | 49 | v 1.0.6 50 | - Unify references to site under site\_name variable. 51 | - Hide updated\_at from uploads and rename getThumbnail() to getThumbnailUrl(). 52 | - Clean up admin/uploads a little. 53 | 54 | v 1.0.5 55 | - Show correct counts on admin user pages and update caches after saving a file. 56 | 57 | v 1.0.4 58 | - Don't call save() after create(), it's redundant. 59 | - Actually strip EXIF info from images and save them to the right file. 60 | 61 | v 1.0.3 62 | - Make the example nginx config a little safer. 63 | 64 | v 1.0.2 65 | - Add email to /u/preferences. 66 | - Fix broken view counter. 67 | - Make user created\_at timestamp use user timezone in /a/users. 68 | 69 | v 1.0.1 70 | - Fixed missing translation for deleting users. 71 | 72 | v 1.0.0 73 | - Started doing actual releases with versions. 74 | - Switched filesystem structure from uploads/$filename to uploads/md5(user\_id)/md5(first\_character\_of\_filename) to help performance on certain filesystems. 75 | 76 | v 1.0.0 Breaking Changes 77 | - Due to the directory structure change, you will be required to run `foreach (\App\Models\Upload::all() as $upload) { $upload->migrate(); }` in the `php artisan tinker` console immediately after upgrade in order for your uploads to be available again. It is recommended you make a backup of storage/app/ first. 78 | -------------------------------------------------------------------------------- /app/Console/Commands/Inspire.php: -------------------------------------------------------------------------------- 1 | comment(PHP_EOL . Inspiring::quote() . PHP_EOL); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Events/Event.php: -------------------------------------------------------------------------------- 1 | shouldReport($e) && php_sapi_name() != 'cli') { 41 | $data = [ 42 | 'ip' => request()->getClientIp(), 43 | 'url' => request()->fullUrl(), 44 | 'exception' => $e->__toString(), 45 | ]; 46 | 47 | Mail::queue(['text' => 'emails.admin.exception'], $data, function (Message $message) use ($data) { 48 | $message->subject('Application Exception'); 49 | $message->to(config('upste.owner_email')); 50 | }); 51 | } 52 | 53 | return parent::report($e); 54 | } 55 | 56 | /** 57 | * Render an exception into an HTTP response. 58 | * 59 | * @param \Illuminate\Http\Request $request 60 | * @param \Exception $e 61 | * @return \Illuminate\Http\Response 62 | */ 63 | public function render($request, Exception $e) 64 | { 65 | if ($e instanceof TokenMismatchException) { 66 | flash()->error('CSRF verification failed, try logging in again.')->important(); 67 | Auth::logout(); 68 | 69 | return redirect()->route('login'); 70 | } 71 | 72 | if ($e instanceof MethodNotAllowedHttpException && $request->getMethod() == 'GET') { 73 | flash()->error('That URL is for POST requests only.'); 74 | 75 | return redirect()->route('account'); 76 | } 77 | 78 | return parent::render($request, $e); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/AccountController.php: -------------------------------------------------------------------------------- 1 | view('account.resources.bash')->header('Content-Type', 'text/plain'); 36 | } 37 | 38 | public function getUploads(Request $request) 39 | { 40 | $uploads = $request->user()->uploads()->orderBy('created_at', 'desc')->paginate($request->user()->preferences->pagination_items); 41 | 42 | return view('account.uploads', compact('uploads')); 43 | } 44 | 45 | public function deleteUpload(Request $request, Upload $upload) 46 | { 47 | if ($request->user()->id !== $upload->user_id && !$request->user()->isPrivilegedUser()) { 48 | return abort(StatusCode::NOT_FOUND); 49 | } 50 | 51 | $upload->forceDelete(); 52 | flash()->success(trans('messages.file_deleted', ['name' => $upload->original_name])); 53 | 54 | return redirect()->route('account.uploads'); 55 | } 56 | 57 | public function deleteAllUploads(Request $request) 58 | { 59 | $uploads = $request->user()->uploads()->get(); 60 | $uploadIds = []; 61 | foreach ($uploads as $upload) { 62 | $upload->deleteDirs(); 63 | $upload->invalidateCache(); 64 | $uploadIds[] = $upload->id; 65 | } 66 | 67 | DB::table('uploads')->whereIn('id', $uploadIds)->delete(); 68 | 69 | $request->user()->invalidateCache(); 70 | 71 | flash()->success(trans('messages.all_uploads_deleted')); 72 | 73 | return redirect()->route('account.uploads'); 74 | } 75 | 76 | public function postResetKey(Request $request) 77 | { 78 | do { 79 | $newKey = str_random(Helpers::API_KEY_LENGTH); 80 | } while (User::whereApikey($newKey)->first()); 81 | 82 | $user = $request->user(); 83 | $user->fill(['apikey' => $newKey])->save(); 84 | flash()->success(trans('messages.api_key_changed', ['api_key' => $newKey]))->important(); 85 | 86 | $passwordRoute = route('account.password.email'); 87 | Mail::queue(['text' => 'emails.user.api_key_reset'], compact('user', 'passwordRoute'), function (Message $message) use ($user) { 88 | $message->subject('API Key Reset'); 89 | $message->to($user->email); 90 | }); 91 | 92 | return redirect()->route('account'); 93 | } 94 | 95 | public function getThumbnail(Request $request, Upload $upload) 96 | { 97 | if (!$request->user() || $request->user()->id !== $upload->user_id && !$request->user()->isPrivilegedUser()) { 98 | return abort(StatusCode::NOT_FOUND); 99 | } 100 | 101 | return response()->download($upload->getThumbnailPath(true), 'thumbnail.' . $upload->name); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/Http/Controllers/Account/PreferencesController.php: -------------------------------------------------------------------------------- 1 | $request->user()->preferences]); 16 | } 17 | 18 | public function post(Request $request) 19 | { 20 | $rules = [ 21 | 'timezone' => 'required|timezone', 22 | 'pagination-items' => 'required|min:4|max:50|integer', 23 | ]; 24 | 25 | $validator = Validator::make($request->all(), $rules); 26 | 27 | if ($validator->fails()) { 28 | return redirect()->route('account.preferences') 29 | ->withErrors($validator); 30 | } 31 | 32 | $request->user()->preferences->fill([ 33 | 'user_id' => Auth::id(), 34 | 'timezone' => $request->input('timezone'), 35 | 'pagination_items' => $request->input('pagination-items'), 36 | ])->save(); 37 | 38 | flash()->success(trans('messages.preferences_saved')); 39 | 40 | return redirect()->route('account.preferences'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Http/Controllers/Admin/AdminController.php: -------------------------------------------------------------------------------- 1 | whereConfirmed(true)->count(); 19 | view()->share('requestCount', $requestCount); 20 | } 21 | } 22 | 23 | public function getIndex() 24 | { 25 | if (config('upste.require_user_approval')) { 26 | return redirect()->route('admin.requests'); 27 | } else { 28 | return redirect()->route('admin.users'); 29 | } 30 | } 31 | 32 | public function getRequests(Request $request) 33 | { 34 | $users = User::whereEnabled(false)->whereConfirmed(true)->orderBy('created_at', 'asc')->paginate($request->user()->preferences->pagination_items); 35 | 36 | return view('admin.requests', compact('users')); 37 | } 38 | 39 | public function getUsers(Request $request) 40 | { 41 | $users = User::whereEnabled(true)->whereConfirmed(true)->paginate($request->user()->preferences->pagination_items); 42 | return view('admin.users', compact('users')); 43 | } 44 | 45 | public function postUserBan(User $user) 46 | { 47 | if ($user->isSuperUser()) { 48 | flash()->error(trans('messages.admin.failed_superuser_action', ['type' => 'ban'])); 49 | 50 | return redirect()->route('admin.users'); 51 | } 52 | 53 | $user->fill(['banned' => true])->save(); 54 | flash()->success(trans('messages.admin.banned_user', ['name' => $user->name])); 55 | 56 | return redirect()->route('admin.users'); 57 | } 58 | 59 | public function postUserDelete(User $user) 60 | { 61 | if ($user->isSuperUser()) { 62 | flash()->error(trans('messages.admin.failed_superuser_action', ['type' => 'delete'])); 63 | 64 | return redirect()->route('admin.users'); 65 | } 66 | 67 | $user->forceDelete(); 68 | flash()->success(trans('messages.admin.deleted_user', ['name' => $user->name])); 69 | 70 | return redirect()->route('admin.users'); 71 | } 72 | 73 | public function postUserUnban(User $user) 74 | { 75 | $user->fill(['banned' => false])->save(); 76 | flash()->success(trans('messages.admin.unbanned_user', ['name' => $user->name])); 77 | 78 | return redirect()->route('admin.users'); 79 | } 80 | 81 | public function getUploads(Request $request, User $user) 82 | { 83 | $allUploads = $user->uploads(); 84 | $uploads = $allUploads->orderBy('created_at', 'desc')->paginate($request->user()->preferences->pagination_items); 85 | 86 | return view('admin.uploads', compact('uploads', 'user')); 87 | } 88 | 89 | public function postUserAccept(User $user) 90 | { 91 | $user->enabled = true; 92 | $user->save(); 93 | 94 | $loginRoute = route('login'); 95 | Mail::queue(['text' => 'emails.user.account_accepted'], compact('user', 'loginRoute'), function (Message $message) use ($user) { 96 | $message->subject('Account Request Accepted'); 97 | $message->to($user->email); 98 | }); 99 | 100 | flash()->success(trans('messages.admin.account_accepted', ['name' => $user->name])); 101 | 102 | return redirect()->route('admin.users'); 103 | } 104 | 105 | public function postUserReject(User $user) 106 | { 107 | Mail::queue(['text' => 'emails.user.account_rejected'], compact('user'), function (Message $message) use ($user) { 108 | $message->subject('Account Request Rejected'); 109 | $message->to($user->email); 110 | }); 111 | 112 | $user->forceDelete(); 113 | flash()->success(trans('messages.admin.account_rejected', ['name' => $user->name])); 114 | 115 | return redirect()->route('admin.users'); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/Http/Controllers/Api/UploadController.php: -------------------------------------------------------------------------------- 1 | hasFile('file')) { 24 | return response()->json([trans('messages.upload_file_not_found')], StatusCode::BAD_REQUEST); 25 | } 26 | 27 | $uploadedFile = $request->file('file'); 28 | if (!$uploadedFile->isValid()) { 29 | return response()->json([trans('messages.invalid_file_upload')], StatusCode::BAD_REQUEST); 30 | } 31 | 32 | if ($uploadedFile->getSize() >= config('upste.upload_limit')) { 33 | $responseMsg = trans('messages.upload_too_large', ['limit' => Helpers::formatBytes(config('upste.upload_limit'))]); 34 | return response()->json([$responseMsg], StatusCode::REQUEST_ENTITY_TOO_LARGE); 35 | } 36 | 37 | // If this upload would hit the quota defined in .env, reject it. 38 | if (config('upste.user_storage_quota') > 0 && !$request->user()->isPrivilegedUser() && ($request->user()->getUploadsSize() + $uploadedFile->getSize()) >= config('upste.user_storage_quota')) { 39 | $responseMsg = trans('messages.reached_upload_limit', ['limit' => Helpers::formatBytes(config('upste.user_storage_quota'))]); 40 | return response()->json([$responseMsg], StatusCode::FORBIDDEN); 41 | } 42 | 43 | $ext = Helpers::detectFileExtension($uploadedFile); 44 | $originalHash = sha1_file($uploadedFile); 45 | $originalName = $uploadedFile->getClientOriginalName(); 46 | 47 | // Check to see if we already have this file for this user. 48 | $existing = Upload::whereOriginalHash($originalHash)->whereUserId($request->user()->id)->first(); 49 | if ($existing) { 50 | $result = [ 51 | 'url' => route('files.get', $existing), 52 | 'delete_url' => route('account.uploads.delete', $existing), 53 | ]; 54 | 55 | $existing->original_name = $originalName; 56 | $existing->save(); 57 | 58 | return response()->json($result, StatusCode::CREATED)->setJsonOptions(JSON_UNESCAPED_SLASHES); 59 | } 60 | 61 | $randomLen = config('upste.upload_slug_length'); 62 | do { 63 | $newName = str_random($randomLen++) . ".$ext"; 64 | } while (Upload::whereName($newName)->first() || $newName === 'index.php'); 65 | 66 | $upload = new Upload([ 67 | 'user_id' => $request->user()->id, 68 | 'name' => $newName, 69 | 'original_name' => $originalName, 70 | 'original_hash' => $originalHash 71 | ]); 72 | 73 | $uploadFileHandle = fopen($uploadedFile->getRealPath(), 'rb'); 74 | Storage::put($upload->getPath(), $uploadFileHandle); 75 | fclose($uploadFileHandle); 76 | 77 | if (Helpers::isImage($uploadedFile)) { 78 | try { 79 | $img = Image::make($uploadedFile); 80 | } catch (NotReadableException $ex) { 81 | Log::error($ex); 82 | 83 | return response()->json([trans('messages.could_not_read_image')], StatusCode::INTERNAL_SERVER_ERROR); 84 | } catch (NotSupportedException $ex) { 85 | Log::error($ex); 86 | 87 | return response()->json([trans('messages.unsupported_image_type')], StatusCode::INTERNAL_SERVER_ERROR); 88 | } 89 | 90 | try { 91 | $upload->createDirs(); 92 | $img->backup(); 93 | $img->fit(128, 128)->save($upload->getThumbnailPath(true)); 94 | $img->reset(); 95 | 96 | if (Helpers::shouldStripExif($uploadedFile)) { 97 | $img->save($upload->getPath(true)); 98 | } 99 | } catch (NotWritableException $ex) { 100 | Log::error($ex); 101 | $upload->deleteDirs(); 102 | 103 | return response()->json([trans('messages.could_not_write_image')], StatusCode::INTERNAL_SERVER_ERROR); 104 | } finally { 105 | $img->destroy(); 106 | } 107 | } 108 | 109 | $savedFile = $upload->getPath(true); 110 | $upload->hash = sha1_file($savedFile); 111 | $upload->size = filesize($savedFile); 112 | 113 | $upload->save(); 114 | 115 | $result = [ 116 | 'url' => route('files.get', $upload), 117 | 'delete_url' => route('account.uploads.delete', $upload), 118 | ]; 119 | 120 | return response()->json($result, StatusCode::CREATED)->setJsonOptions(JSON_UNESCAPED_SLASHES); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/PasswordController.php: -------------------------------------------------------------------------------- 1 | redirectTo = route('account'); 36 | } 37 | 38 | /** 39 | * Send a reset link to the given user. 40 | * 41 | * @param \Illuminate\Http\Request $request 42 | * @return \Illuminate\Http\Response 43 | */ 44 | public function sendResetLinkEmail(Request $request) 45 | { 46 | $rules = [ 47 | 'email' => 'required|email', 48 | ]; 49 | 50 | if (config('upste.recaptcha_enabled')) { 51 | $rules['g-recaptcha-response'] = 'required|recaptcha'; 52 | } 53 | 54 | $this->validate($request, $rules); 55 | 56 | $broker = $this->getBroker(); 57 | 58 | $passwordRoute = route('account.password.reset'); 59 | view()->composer('emails.user.password_reset', function ($view) use ($passwordRoute) { 60 | $view->with(compact('passwordRoute')); 61 | }); 62 | 63 | $response = Password::broker($broker)->sendResetLink($request->only('email'), function (Message $message) { 64 | $message->subject($this->getEmailSubject()); 65 | }); 66 | 67 | switch ($response) { 68 | case Password::RESET_LINK_SENT: 69 | return $this->getSendResetLinkEmailSuccessResponse($response); 70 | 71 | case Password::INVALID_USER: 72 | default: 73 | return $this->getSendResetLinkEmailFailureResponse($response); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | getPath())) { 20 | if ($upload->user->banned) { 21 | Log::info('Refusing to serve file for banned user.', ['user' => $upload->user->name, 'file' => $upload->name]); 22 | return abort(StatusCode::NOT_FOUND); 23 | } 24 | 25 | if (!$request->user() || $request->user()->id !== $upload->user_id) { 26 | $cacheKey = sprintf('cached_view:%s:%s', $request->ip(), $upload->name); 27 | if (!Cache::has($cacheKey)) { 28 | Cache::put($cacheKey, 1, 60); 29 | DB::table('uploads')->where('id', $upload->id)->increment('views'); 30 | } 31 | } 32 | 33 | return Helpers::sendFile($upload); 34 | } 35 | 36 | return abort(StatusCode::NOT_FOUND); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Http/Controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | route('account'); 21 | } 22 | 23 | return view('index'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | ApiAuthenticate::class, 41 | 'admin' => AdminAuthenticate::class, 42 | 'auth' => Authenticate::class, 43 | 'auth.basic' => AuthenticateWithBasicAuth::class, 44 | 'guest' => RedirectIfAuthenticated::class, 45 | ]; 46 | } 47 | -------------------------------------------------------------------------------- /app/Http/Middleware/AdminAuthenticate.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 29 | } 30 | 31 | /** 32 | * Handle an incoming request. 33 | * 34 | * @param \Illuminate\Http\Request $request 35 | * @param \Closure $next 36 | * @return mixed 37 | */ 38 | public function handle(Request $request, Closure $next) 39 | { 40 | if (!$this->auth->check()) { 41 | flash()->error('You must log in to access that page.'); 42 | 43 | return redirect()->route('login'); 44 | } 45 | 46 | if (!$request->user()->isPrivilegedUser()) { 47 | flash()->error('You do not have permission to access that area.'); 48 | 49 | return redirect()->route('index'); 50 | } 51 | 52 | return $next($request); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/Http/Middleware/ApiAuthenticate.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 30 | } 31 | 32 | /** 33 | * Handle an incoming request. 34 | * 35 | * @param \Illuminate\Http\Request $request 36 | * @param \Closure $next 37 | * @return mixed 38 | */ 39 | public function handle(Request $request, Closure $next) 40 | { 41 | if (!$request->has('key')) { 42 | return response()->json(['missing_api_key'], StatusCode::UNAUTHORIZED); 43 | } 44 | 45 | $apiKey = $request->input('key'); 46 | $user = User::whereApikey($apiKey)->first(); 47 | if (!$user) { 48 | return response()->json(['invalid_api_key'], StatusCode::UNAUTHORIZED); 49 | } 50 | 51 | if (config('upste.require_email_verification') && !$user->confirmed) { 52 | return response()->json(['email_not_confirmed'], StatusCode::UNAUTHORIZED); 53 | } 54 | 55 | if (config('upste.require_user_approval') && !$user->enabled) { 56 | return response()->json(['account_not_approved'], StatusCode::UNAUTHORIZED); 57 | } 58 | 59 | if ($user->banned) { 60 | return response()->json(['user_banned'], StatusCode::UNAUTHORIZED); 61 | } 62 | 63 | Auth::onceUsingId($user->id); 64 | 65 | return $next($request); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 30 | } 31 | 32 | /** 33 | * Handle an incoming request. 34 | * 35 | * @param \Illuminate\Http\Request $request 36 | * @param \Closure $next 37 | * @return mixed 38 | */ 39 | public function handle(Request $request, Closure $next) 40 | { 41 | if (!$this->auth->check()) { 42 | Auth::logout(); 43 | Session::flush(); 44 | flash()->error(trans('messages.not_logged_in'))->important(); 45 | 46 | return redirect()->route('login'); 47 | } 48 | 49 | if (config('upste.require_email_verification') && !$request->user()->confirmed) { 50 | Auth::logout(); 51 | Session::flush(); 52 | flash()->error(trans('messages.not_confirmed'))->important(); 53 | 54 | return redirect()->route('login'); 55 | } 56 | 57 | if (config('upste.require_user_approval') && !$request->user()->enabled) { 58 | Auth::logout(); 59 | Session::flush(); 60 | flash()->error(trans('messages.not_activated'))->important(); 61 | 62 | return redirect()->route('login'); 63 | } 64 | 65 | if ($request->user()->banned) { 66 | Auth::logout(); 67 | Session::flush(); 68 | flash()->error(trans('messages.not_activated'))->important(); 69 | 70 | return redirect()->route('login'); 71 | } 72 | 73 | return $next($request); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 27 | } 28 | 29 | /** 30 | * Handle an incoming request. 31 | * 32 | * @param \Illuminate\Http\Request $request 33 | * @param \Closure $next 34 | * @return mixed 35 | */ 36 | public function handle(Request $request, Closure $next) 37 | { 38 | if ($this->auth->check()) { 39 | return redirect()->route('account'); 40 | } 41 | 42 | return $next($request); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | make()->header('X-Accel-Redirect', '/' . $upload->getPath()) 31 | ->header('Content-Type', '') 32 | ->header('Content-Disposition', sprintf('inline; filename="%s"', $upload->original_name)); 33 | break; 34 | case 'x-sendfile': 35 | return response()->make()->header('X-Sendfile', $upload->getPath(true)) 36 | ->header('Content-Type', '') 37 | ->header('Content-Disposition', sprintf('inline; filename="%s"', $upload->original_name)); 38 | break; 39 | default: 40 | return response()->file($upload->getPath(true), ['Content-Disposition' => sprintf('inline; filename="%s"', $upload->original_name)]); 41 | } 42 | } 43 | 44 | // http://stackoverflow.com/questions/2510434/format-bytes-to-kilobytes-megabytes-gigabytes 45 | /** 46 | * @param $bytes 47 | * @param int $precision 48 | * @return string 49 | */ 50 | public static function formatBytes($bytes, $precision = 2) 51 | { 52 | $units = ['B', 'KB', 'MB', 'GB', 'TB']; 53 | 54 | $bytes = max($bytes, 0); 55 | $pow = floor(($bytes ? log($bytes) : 0) / log(1000)); 56 | $pow = min($pow, count($units) - 1); 57 | $bytes /= pow(1000, $pow); 58 | 59 | return round($bytes, $precision) . ' ' . $units[$pow]; 60 | } 61 | 62 | /** 63 | * Checks whether or not it's safe to attempt to strip exif tags from a file 64 | * 65 | * @param $file 66 | * @return bool 67 | */ 68 | public static function shouldStripExif(UploadedFile $file) 69 | { 70 | if (!config('upste.strip_exif')) { 71 | return false; 72 | } 73 | 74 | if (function_exists('exif_imagetype')) { 75 | try { 76 | switch (exif_imagetype($file)) { 77 | case IMAGETYPE_JPEG: 78 | case IMAGETYPE_PNG: 79 | return true; 80 | break; 81 | default: 82 | return false; 83 | break; 84 | } 85 | } catch (Exception $ex) { 86 | Log::error($ex->getMessage()); 87 | 88 | return false; 89 | } 90 | } 91 | 92 | return false; 93 | } 94 | 95 | public static function isImage(UploadedFile $file) 96 | { 97 | if (function_exists('exif_imagetype')) { 98 | try { 99 | switch (exif_imagetype($file)) { 100 | case IMAGETYPE_JPEG: 101 | case IMAGETYPE_PNG: 102 | case IMAGETYPE_GIF: 103 | return true; 104 | break; 105 | default: 106 | return false; 107 | break; 108 | } 109 | } catch (Exception $ex) { 110 | Log::error($ex); 111 | 112 | return false; 113 | } 114 | } 115 | 116 | return false; 117 | } 118 | 119 | public static function properize($string) 120 | { 121 | return $string . '\'' . (ends_with($string, 's') ? '' : 's'); 122 | } 123 | 124 | public static function detectFileExtension(UploadedFile $file) 125 | { 126 | if ($file->getClientOriginalExtension()) { 127 | return $file->getClientOriginalExtension(); 128 | } 129 | 130 | $mimeTypes = new MimeTypes; 131 | $extension = $mimeTypes->getExtension(mime_content_type($file->getRealPath())); 132 | if ($extension) { 133 | if ($extension == 'jpeg') { 134 | return 'jpg'; 135 | } 136 | 137 | return $extension; 138 | } 139 | 140 | return 'txt'; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/Http/routes.php: -------------------------------------------------------------------------------- 1 | 'guest'], function () { 18 | Route::get('/', [ 19 | 'as' => 'index', 'uses' => 'IndexController@getIndex']); 20 | 21 | Route::get('login', [ 22 | 'as' => 'login', 'uses' => 'Auth\AuthController@getLogin']); 23 | 24 | Route::post('login', [ 25 | 'as' => 'login', 'uses' => 'Auth\AuthController@postLogin']); 26 | 27 | Route::get('register', [ 28 | 'as' => 'register', 'uses' => 'Auth\AuthController@getRegister']); 29 | 30 | Route::post('register', [ 31 | 'as' => 'register', 'uses' => 'Auth\AuthController@postRegister']); 32 | 33 | if (config('upste.require_email_verification')) { 34 | Route::get('register/verify/{confirmationCode}', [ 35 | 'as' => 'register.confirmation', 'uses' => 'Auth\AuthController@confirm' 36 | ]); 37 | } 38 | 39 | Route::group(['prefix' => 'password'], function () { 40 | Route::get('email', [ 41 | 'as' => 'account.password.email', 'uses' => 'Auth\PasswordController@getEmail']); 42 | 43 | Route::post('email', [ 44 | 'as' => 'account.password.email', 'uses' => 'Auth\PasswordController@postEmail']); 45 | 46 | Route::get('reset/{token}', [ 47 | 'as' => 'account.password.reset', 'uses' => 'Auth\PasswordController@getReset']); 48 | 49 | Route::post('reset', [ 50 | 'as' => 'account.password.reset', 'uses' => 'Auth\PasswordController@postReset']); 51 | }); 52 | }); 53 | 54 | /* 55 | * Routes only authenticated users can access 56 | */ 57 | Route::group(['middleware' => 'auth', 'prefix' => 'u'], function () { 58 | Route::get('/', [ 59 | 'as' => 'account', 'uses' => 'Account\AccountController@getindex']); 60 | 61 | Route::get('logout', [ 62 | 'as' => 'logout', 'uses' => 'Auth\AuthController@getLogout']); 63 | 64 | Route::get('resources', [ 65 | 'as' => 'account.resources', 'uses' => 'Account\AccountController@getResources']); 66 | 67 | Route::get('resources/scripts/bash', [ 68 | 'as' => 'account.resources.bash', 'uses' => 'Account\AccountController@getBashScript']); 69 | 70 | Route::get('faq', [ 71 | 'as' => 'account.faq', 'uses' => 'Account\AccountController@getFaq']); 72 | 73 | Route::get('preferences', [ 74 | 'as' => 'account.preferences', 'uses' => 'Account\PreferencesController@get']); 75 | 76 | Route::post('preferences', [ 77 | 'as' => 'account.preferences', 'uses' => 'Account\PreferencesController@post']); 78 | 79 | Route::group(['prefix' => 'uploads'], function () { 80 | Route::get('/', [ 81 | 'as' => 'account.uploads', 'uses' => 'Account\AccountController@getUploads']); 82 | 83 | Route::post('/delete', [ 84 | 'as' => 'account.uploads.delete_all', 'uses' => 'Account\AccountController@deleteAllUploads']); 85 | 86 | Route::post('{upload}/delete', [ 87 | 'as' => 'account.uploads.delete', 'uses' => 'Account\AccountController@deleteUpload']); 88 | 89 | Route::get('{upload}/delete', [ 90 | 'as' => 'account.uploads.delete', 'uses' => 'Account\AccountController@deleteUpload']); 91 | 92 | Route::get('{upload}/thumbnail', [ 93 | 'as' => 'account.uploads.thumbnail', 'uses' => 'Account\AccountController@getThumbnail']); 94 | }); 95 | 96 | Route::post('resetkey', [ 97 | 'as' => 'account.resetkey', 'uses' => 'Account\AccountController@postResetKey']); 98 | }); 99 | 100 | /* 101 | * Routes only admins can access 102 | */ 103 | Route::group(['middleware' => 'admin', 'prefix' => 'a'], function () { 104 | 105 | Route::get('/', [ 106 | 'as' => 'admin', 'uses' => 'Admin\AdminController@getIndex']); 107 | 108 | Route::group(['prefix' => 'users'], function () { 109 | Route::get('/', [ 110 | 'as' => 'admin.users', 'uses' => 'Admin\AdminController@getUsers']); 111 | 112 | Route::post('{user}/ban', [ 113 | 'as' => 'admin.users.ban', 'uses' => 'Admin\AdminController@postUserBan']); 114 | 115 | Route::post('{user}/unban', [ 116 | 'as' => 'admin.users.unban', 'uses' => 'Admin\AdminController@postUserUnban']); 117 | 118 | Route::get('{user}/uploads', [ 119 | 'as' => 'admin.users.uploads', 'uses' => 'Admin\AdminController@getUploads']); 120 | 121 | Route::post('{user}/delete', [ 122 | 'as' => 'admin.users.delete', 'uses' => 'Admin\AdminController@postUserDelete']); 123 | 124 | if (config('upste.require_user_approval')) { 125 | Route::get('requests', [ 126 | 'as' => 'admin.requests', 'uses' => 'Admin\AdminController@getRequests']); 127 | 128 | Route::post('{user}/accept', [ 129 | 'as' => 'admin.users.accept', 'uses' => 'Admin\AdminController@postUserAccept']); 130 | 131 | Route::post('{user}/reject', [ 132 | 'as' => 'admin.users.reject', 'uses' => 'Admin\AdminController@postUserReject']); 133 | } 134 | }); 135 | }); 136 | 137 | /* 138 | * API routes 139 | */ 140 | Route::group(['middleware' => 'api', 'prefix' => 'api'], function () { 141 | Route::post('upload', [ 142 | 'as' => 'api.upload', 'uses' => 'Api\UploadController@post']); 143 | }); 144 | 145 | /* 146 | * Uploads 147 | */ 148 | Route::group(['domain' => config('upste.upload_domain')], function () { 149 | Route::get('{upload}', [ 150 | 'as' => 'files.get', 'uses' => 'DownloadController@get']); 151 | }); 152 | -------------------------------------------------------------------------------- /app/Jobs/Job.php: -------------------------------------------------------------------------------- 1 | sum('size'); 57 | }); 58 | } 59 | 60 | public static function count() 61 | { 62 | return Cache::rememberForever('uploads', function () { 63 | return DB::table('uploads')->count(); 64 | }); 65 | } 66 | 67 | public function save(array $options = []) 68 | { 69 | $this->invalidateCache(); 70 | $this->user->invalidateCache(); 71 | 72 | return parent::save($options); 73 | } 74 | 75 | public function invalidateCache() 76 | { 77 | Cache::forget('uploads'); 78 | Cache::forget('uploads_total_size'); 79 | } 80 | 81 | /** 82 | * Get the user that owns the upload. 83 | */ 84 | public function user() 85 | { 86 | return $this->belongsTo(User::class); 87 | } 88 | 89 | public function forceDelete() 90 | { 91 | $this->deleteDirs(); 92 | $this->invalidateCache(); 93 | $this->user->invalidateCache(); 94 | 95 | return parent::forceDelete(); 96 | } 97 | 98 | public function deleteDirs() 99 | { 100 | $path = $this->getPath(); 101 | $dir = $this->getDir(); 102 | if (Storage::exists($path)) { 103 | Storage::delete($path); 104 | if (!count(Storage::files($dir))) { 105 | Storage::deleteDir($dir); 106 | } 107 | } 108 | 109 | $path = $this->getThumbnailPath(); 110 | $dir = $this->getThumbnailDir(); 111 | if (Storage::exists($path)) { 112 | Storage::delete($path); 113 | if (!count(Storage::files($dir))) { 114 | Storage::deleteDir($dir); 115 | } 116 | } 117 | } 118 | 119 | public function getPath($fullPath = false) 120 | { 121 | return $this->getDir($fullPath) . $this->name; 122 | } 123 | 124 | public function getDir($fullDir = false) 125 | { 126 | if ($fullDir) { 127 | return storage_path(sprintf('app/uploads/%s/%s/', md5($this->user_id), md5(mb_substr($this->name, 0, 1, 'utf-8')))); 128 | } 129 | 130 | return sprintf('uploads/%s/%s/', md5($this->user_id), md5(mb_substr($this->name, 0, 1, 'utf-8'))); 131 | } 132 | 133 | public function getThumbnailPath($fullPath = false) 134 | { 135 | return $this->getThumbnailDir($fullPath) . $this->name; 136 | } 137 | 138 | public function getThumbnailDir($fullDir = false) 139 | { 140 | if ($fullDir) { 141 | return storage_path(sprintf('app/thumbnails/%s/%s/', md5($this->user_id), md5(mb_substr($this->name, 0, 1, 'utf-8')))); 142 | } 143 | 144 | return sprintf('thumbnails/%s/%s/', md5($this->user_id), md5(mb_substr($this->name, 0, 1, 'utf-8'))); 145 | } 146 | 147 | /** 148 | * Migrates files from the old uploads/$filename structure to a more efficient one. 149 | * Should only be called once from the command line. 150 | */ 151 | public function migrate() 152 | { 153 | if (php_sapi_name() !== 'cli') { 154 | trigger_error('CLI-only (Upload#migrate()) function called outside of CLI SAPI.', E_USER_ERROR); 155 | 156 | return; 157 | } 158 | 159 | $this->createDirs(); 160 | 161 | if (Storage::exists('uploads/' . $this->name)) { 162 | Storage::move('uploads/' . $this->name, $this->getPath()); 163 | } 164 | 165 | if (Storage::exists('thumbnails/' . $this->name)) { 166 | Storage::move('thumbnails/' . $this->name, $this->getThumbnailPath()); 167 | } 168 | } 169 | 170 | public function createDirs() 171 | { 172 | if (!Storage::exists($this->getDir())) { 173 | Storage::makeDirectory($this->getDir()); 174 | } 175 | 176 | if (!Storage::exists($this->getThumbnailDir())) { 177 | Storage::makeDirectory($this->getThumbnailDir()); 178 | } 179 | } 180 | 181 | public function hasPreview() 182 | { 183 | return $this->getThumbnailUrl() !== elixir('assets/img/thumbnail.png'); 184 | } 185 | 186 | public function getThumbnailUrl() 187 | { 188 | if (Storage::exists($this->getThumbnailDir() . $this->name)) { 189 | return route('account.uploads.thumbnail', $this); 190 | } 191 | 192 | return elixir('assets/img/thumbnail.png'); 193 | } 194 | 195 | public function getRouteKeyName() 196 | { 197 | return 'name'; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | where('enabled', true)->where('confirmed', true)->count(); 74 | }); 75 | } 76 | 77 | /** 78 | * Get this user's uploads. 79 | */ 80 | public function uploads() 81 | { 82 | return $this->hasMany(Upload::class); 83 | } 84 | 85 | public function preferences() 86 | { 87 | return $this->hasOne(UserPreferences::class); 88 | } 89 | 90 | public function forceDelete() 91 | { 92 | foreach ($this->uploads as $upload) { 93 | $upload->forceDelete(); 94 | } 95 | 96 | if (Storage::exists('uploads/' . md5($this->id))) { 97 | Storage::deleteDirectory('uploads/' . md5($this->id)); 98 | } 99 | 100 | if (Storage::exists('thumbnails/' . md5($this->id))) { 101 | Storage::deleteDirectory('thumbnails/' . md5($this->id)); 102 | } 103 | 104 | $this->invalidateCache(); 105 | $this->invalidateGlobalCache(); 106 | 107 | return parent::forceDelete(); 108 | } 109 | 110 | public function invalidateCache() 111 | { 112 | Cache::forget('uploads_size:' . $this->id); 113 | Cache::forget('uploads_count:' . $this->id); 114 | } 115 | 116 | private function invalidateGlobalCache() { 117 | Cache::forget('users'); 118 | } 119 | 120 | public function setEnabledAttribute($value) 121 | { 122 | $this->attributes['enabled'] = $value; 123 | $this->invalidateGlobalCache(); 124 | } 125 | 126 | public function setConfirmedAttribute($value) 127 | { 128 | $this->attributes['confirmed'] = $value; 129 | $this->invalidateGlobalCache(); 130 | } 131 | 132 | public function getUploadsCount() 133 | { 134 | return Cache::rememberForever('uploads_count:' . $this->id, function () { 135 | return $this->uploads->count(); 136 | }); 137 | } 138 | 139 | public function getStorageQuota() 140 | { 141 | $userStorageQuota = Helpers::formatBytes($this->getUploadsSize()); 142 | if (config('upste.user_storage_quota') > 0 && !$this->isPrivilegedUser()) { 143 | $userStorageQuota = sprintf("%s / %s", $userStorageQuota, Helpers::formatBytes(config('upste.user_storage_quota'))); 144 | } 145 | 146 | return $userStorageQuota; 147 | } 148 | 149 | public function getUploadsSize() 150 | { 151 | return Cache::rememberForever('uploads_size:' . $this->id, function () { 152 | return $this->uploads->sum('size'); 153 | }); 154 | } 155 | 156 | public function isPrivilegedUser() 157 | { 158 | return $this->admin || $this->isSuperUser(); 159 | } 160 | 161 | public function isSuperUser() 162 | { 163 | return $this->id === 1; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/Models/UserPreferences.php: -------------------------------------------------------------------------------- 1 | belongsTo('App\Models\User'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.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 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | model('user', 'App\Models\User'); 31 | $router->model('upload', 'App\Models\Upload'); 32 | } 33 | 34 | /** 35 | * Define the routes for the application. 36 | * 37 | * @param \Illuminate\Routing\Router $router 38 | * @return void 39 | */ 40 | public function map(Router $router) 41 | { 42 | $router->group(['namespace' => $this->namespace], function ($router) { 43 | require app_path('Http/routes.php'); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bootstrap/autoload.php: -------------------------------------------------------------------------------- 1 | =5.5.9", 23 | "laravel/framework": "5.2.*", 24 | "barryvdh/laravel-debugbar": "v2.2.*", 25 | "barryvdh/laravel-ide-helper": "v2.1.*", 26 | "doctrine/dbal": "2.3.*", 27 | "laracasts/flash": "1.3.*", 28 | "shrikeh/teapot": "1.0.*", 29 | "intervention/image": "^2.3", 30 | "greggilbert/recaptcha": "^2.1", 31 | "msurguy/honeypot": "^1.0", 32 | "predis/predis": "^1.1", 33 | "ralouphie/mimey": "^1.0" 34 | }, 35 | "require-dev": { 36 | "fzaninotto/faker": "~1.4", 37 | "mockery/mockery": "0.9.*", 38 | "phpunit/phpunit": "~4.0", 39 | "phpspec/phpspec": "~2.1", 40 | "symfony/css-selector": "~3.0", 41 | "symfony/dom-crawler": "~3.0" 42 | 43 | }, 44 | "autoload": { 45 | "classmap": [ 46 | "database" 47 | ], 48 | "files": [ 49 | "app/Http/helpers.php" 50 | ], 51 | "psr-4": { 52 | "App\\": "app/" 53 | } 54 | }, 55 | "autoload-dev": { 56 | "classmap": [ 57 | "tests/TestCase.php" 58 | ] 59 | }, 60 | "scripts": { 61 | "post-install-cmd": [ 62 | "php -r \"copy('.env.example', '.env');\"", 63 | "php artisan key:generate", 64 | "npm install", 65 | "gulp --production", 66 | "php artisan optimize" 67 | ], 68 | "pre-update-cmd": [ 69 | "php artisan down" 70 | ], 71 | "post-update-cmd": [ 72 | "Illuminate\\Foundation\\ComposerScripts::postUpdate", 73 | "php artisan ide-helper:generate", 74 | "php artisan ide-helper:meta", 75 | "npm update", 76 | "gulp --production", 77 | "php artisan migrate", 78 | "php artisan optimize", 79 | "php artisan cache:clear", 80 | "php artisan config:cache", 81 | "php artisan view:clear", 82 | "php artisan route:cache", 83 | "php artisan queue:restart", 84 | "php artisan up" 85 | ], 86 | "cs": "phpcs --standard=PSR2 app/ || true", 87 | "cbf": "phpcbf --standard=PSR2 app/ || true", 88 | "recache": [ 89 | "php artisan down", 90 | "php artisan optimize", 91 | "php artisan cache:clear", 92 | "php artisan config:cache", 93 | "php artisan view:clear", 94 | "php artisan route:cache", 95 | "php artisan up" 96 | ] 97 | }, 98 | "config": { 99 | "preferred-install": "dist" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /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' => 'emails.user.password_reset', 102 | 'table' => 'password_resets', 103 | 'expire' => 60, 104 | ], 105 | ], 106 | 107 | ]; 108 | -------------------------------------------------------------------------------- /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 | ], 37 | 38 | 'redis' => [ 39 | 'driver' => 'redis', 40 | 'connection' => 'default', 41 | ], 42 | 43 | 'log' => [ 44 | 'driver' => 'log', 45 | ], 46 | 47 | ], 48 | 49 | ]; 50 | -------------------------------------------------------------------------------- /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' => '127.0.0.1', 'port' => 11211, 'weight' => 100, 55 | ], 56 | ], 57 | ], 58 | 59 | 'redis' => [ 60 | 'driver' => 'redis', 61 | 'connection' => 'default', 62 | ], 63 | 64 | ], 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Cache Key Prefix 69 | |-------------------------------------------------------------------------- 70 | | 71 | | When utilizing a RAM based store such as APC or Memcached, there might 72 | | be other applications utilizing the same cache. So, we'll specify a 73 | | value to get prefixed to all our keys so we can avoid collisions. 74 | | 75 | */ 76 | 77 | 'prefix' => 'pste', 78 | 79 | ]; 80 | -------------------------------------------------------------------------------- /config/compile.php: -------------------------------------------------------------------------------- 1 | [ 17 | ], 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Compiled File Providers 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may list service providers which define a "compiles" function 25 | | that returns additional files that should be compiled, providing an 26 | | easy way to get common files from any packages you are utilizing. 27 | | 28 | */ 29 | 30 | 'providers' => [ 31 | // 32 | ], 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /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' => storage_path('database.sqlite'), 52 | 'prefix' => '', 53 | ], 54 | 55 | 'mysql' => [ 56 | 'driver' => 'mysql', 57 | 'host' => env('DB_HOST', 'localhost'), 58 | 'database' => env('DB_DATABASE', 'forge'), 59 | 'username' => env('DB_USERNAME', 'forge'), 60 | 'password' => env('DB_PASSWORD', ''), 61 | 'charset' => 'utf8', 62 | 'collation' => 'utf8_bin', 63 | 'prefix' => '', 64 | 'strict' => false, 65 | ], 66 | 67 | 'pgsql' => [ 68 | 'driver' => 'pgsql', 69 | 'host' => env('DB_HOST', 'localhost'), 70 | 'database' => env('DB_DATABASE', 'forge'), 71 | 'username' => env('DB_USERNAME', 'forge'), 72 | 'password' => env('DB_PASSWORD', ''), 73 | 'charset' => 'utf8', 74 | 'prefix' => '', 75 | 'schema' => 'public', 76 | ], 77 | 78 | 'sqlsrv' => [ 79 | 'driver' => 'sqlsrv', 80 | 'host' => env('DB_HOST', 'localhost'), 81 | 'database' => env('DB_DATABASE', 'forge'), 82 | 'username' => env('DB_USERNAME', 'forge'), 83 | 'password' => env('DB_PASSWORD', ''), 84 | 'charset' => 'utf8', 85 | 'prefix' => '', 86 | ], 87 | 88 | ], 89 | 90 | /* 91 | |-------------------------------------------------------------------------- 92 | | Migration Repository Table 93 | |-------------------------------------------------------------------------- 94 | | 95 | | This table keeps track of all the migrations that have already run for 96 | | your application. Using this information, we can determine which of 97 | | the migrations on disk haven't actually been run in the database. 98 | | 99 | */ 100 | 101 | 'migrations' => 'migrations', 102 | 103 | /* 104 | |-------------------------------------------------------------------------- 105 | | Redis Databases 106 | |-------------------------------------------------------------------------- 107 | | 108 | | Redis is an open source, fast, and advanced key-value store that also 109 | | provides a richer set of commands than a typical key-value systems 110 | | such as APC or Memcached. Laravel makes it easy to dig right in. 111 | | 112 | */ 113 | 114 | 'redis' => [ 115 | 116 | 'cluster' => false, 117 | 118 | 'default' => [ 119 | 'host' => env('REDIS_HOST', '127.0.0.1'), 120 | 'path' => env('REDIS_PATH', '/tmp/redis.sock'), 121 | 'port' => env('REDIS_PORT', 6379), 122 | 'database' => env('REDIS_DATABASE', 0), 123 | 'scheme' => env('REDIS_SCHEME', 'tcp'), 124 | 'password' => env('REDIS_PASSWORD', null), 125 | ], 126 | 127 | ], 128 | 129 | ]; 130 | -------------------------------------------------------------------------------- /config/debugbar.php: -------------------------------------------------------------------------------- 1 | null, 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Storage settings 20 | |-------------------------------------------------------------------------- 21 | | 22 | | DebugBar stores data for session/ajax requests. 23 | | You can disable this, so the debugbar stores data in headers/session, 24 | | but this can cause problems with large data collectors. 25 | | By default, file storage (in the storage folder) is used. Redis and PDO 26 | | can also be used. For PDO, run the package migrations first. 27 | | 28 | */ 29 | 'storage' => [ 30 | 'enabled' => true, 31 | 'driver' => 'file', // redis, file, pdo 32 | 'path' => storage_path() . '/debugbar', // For file driver 33 | 'connection' => null, // Leave null for default connection (Redis/PDO) 34 | ], 35 | 36 | /* 37 | |-------------------------------------------------------------------------- 38 | | Vendors 39 | |-------------------------------------------------------------------------- 40 | | 41 | | Vendor files are included by default, but can be set to false. 42 | | This can also be set to 'js' or 'css', to only include javascript or css vendor files. 43 | | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) 44 | | and for js: jquery and and highlight.js 45 | | So if you want syntax highlighting, set it to true. 46 | | jQuery is set to not conflict with existing jQuery scripts. 47 | | 48 | */ 49 | 50 | 'include_vendors' => true, 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Capture Ajax Requests 55 | |-------------------------------------------------------------------------- 56 | | 57 | | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), 58 | | you can use this option to disable sending the data through the headers. 59 | | 60 | */ 61 | 62 | 'capture_ajax' => true, 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Clockwork integration 67 | |-------------------------------------------------------------------------- 68 | | 69 | | The Debugbar can emulate the Clockwork headers, so you can use the Chrome 70 | | Extension, without the server-side code. It uses Debugbar collectors instead. 71 | | 72 | */ 73 | 'clockwork' => false, 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | DataCollectors 78 | |-------------------------------------------------------------------------- 79 | | 80 | | Enable/disable DataCollectors 81 | | 82 | */ 83 | 84 | 'collectors' => [ 85 | 'phpinfo' => true, // Php version 86 | 'messages' => true, // Messages 87 | 'time' => true, // Time Datalogger 88 | 'memory' => true, // Memory usage 89 | 'exceptions' => true, // Exception displayer 90 | 'log' => true, // Logs from Monolog (merged in messages if enabled) 91 | 'db' => true, // Show database (PDO) queries and bindings 92 | 'views' => true, // Views with their data 93 | 'route' => true, // Current route information 94 | 'laravel' => false, // Laravel version and environment 95 | 'events' => false, // All events fired 96 | 'default_request' => false, // Regular or special Symfony request logger 97 | 'symfony_request' => true, // Only one can be enabled.. 98 | 'mail' => true, // Catch mail messages 99 | 'logs' => false, // Add the latest log messages 100 | 'files' => false, // Show the included files 101 | 'config' => false, // Display config settings 102 | 'auth' => false, // Display Laravel authentication status 103 | 'session' => true, // Display session data 104 | ], 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Extra options 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Configure some DataCollectors 112 | | 113 | */ 114 | 115 | 'options' => [ 116 | 'auth' => [ 117 | 'show_name' => false, // Also show the users name/email in the debugbar 118 | ], 119 | 'db' => [ 120 | 'with_params' => true, // Render SQL with the parameters substituted 121 | 'timeline' => false, // Add the queries to the timeline 122 | 'backtrace' => false, // EXPERIMENTAL: Use a backtrace to find the origin of the query in your files. 123 | 'explain' => [ // EXPERIMENTAL: Show EXPLAIN output on queries 124 | 'enabled' => false, 125 | 'types' => ['SELECT'], // array('SELECT', 'INSERT', 'UPDATE', 'DELETE'); for MySQL 5.6.3+ 126 | ], 127 | 'hints' => true, // Show hints for common mistakes 128 | ], 129 | 'mail' => [ 130 | 'full_log' => false 131 | ], 132 | 'views' => [ 133 | 'data' => false, //Note: Can slow down the application, because the data can be quite large.. 134 | ], 135 | 'route' => [ 136 | 'label' => true // show complete route on bar 137 | ], 138 | 'logs' => [ 139 | 'file' => null 140 | ], 141 | ], 142 | 143 | /* 144 | |-------------------------------------------------------------------------- 145 | | Inject Debugbar in Response 146 | |-------------------------------------------------------------------------- 147 | | 148 | | Usually, the debugbar is added just before , by listening to the 149 | | Response after the App is done. If you disable this, you have to add them 150 | | in your template yourself. See http://phpdebugbar.com/docs/rendering.html 151 | | 152 | */ 153 | 154 | 'inject' => true, 155 | 156 | /* 157 | |-------------------------------------------------------------------------- 158 | | DebugBar route prefix 159 | |-------------------------------------------------------------------------- 160 | | 161 | | Sometimes you want to set route prefix to be used by DebugBar to load 162 | | its resources from. Usually the need comes from misconfigured web server or 163 | | from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 164 | | 165 | */ 166 | 'route_prefix' => '_debugbar', 167 | 168 | ]; 169 | -------------------------------------------------------------------------------- /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 | 'ftp' => [ 52 | 'driver' => 'ftp', 53 | 'host' => 'ftp.example.com', 54 | 'username' => 'your-username', 55 | 'password' => 'your-password', 56 | 57 | // Optional FTP Settings... 58 | // 'port' => 21, 59 | // 'root' => '', 60 | // 'passive' => true, 61 | // 'ssl' => true, 62 | // 'timeout' => 30, 63 | ], 64 | 65 | 's3' => [ 66 | 'driver' => 's3', 67 | 'key' => 'your-key', 68 | 'secret' => 'your-secret', 69 | 'region' => 'your-region', 70 | 'bucket' => 'your-bucket', 71 | ], 72 | 73 | 'rackspace' => [ 74 | 'driver' => 'rackspace', 75 | 'username' => 'your-username', 76 | 'key' => 'your-key', 77 | 'container' => 'your-container', 78 | 'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/', 79 | 'region' => 'IAD', 80 | 'url_type' => 'publicURL', 81 | ], 82 | 83 | ], 84 | 85 | ]; 86 | -------------------------------------------------------------------------------- /config/ide-helper.php: -------------------------------------------------------------------------------- 1 | '_ide_helper', 15 | 'format' => 'php', 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Helper files to include 20 | |-------------------------------------------------------------------------- 21 | | 22 | | Include helper files. By default not included, but can be toggled with the 23 | | -- helpers (-H) option. Extra helper files can be included. 24 | | 25 | */ 26 | 27 | 'include_helpers' => false, 28 | 29 | 'helper_files' => [ 30 | base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php', 31 | ], 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Model locations to include 36 | |-------------------------------------------------------------------------- 37 | | 38 | | Define in which directories the ide-helper:models command should look 39 | | for models. 40 | | 41 | */ 42 | 43 | 'model_locations' => [ 44 | 'app', 45 | ], 46 | 47 | 48 | /* 49 | |-------------------------------------------------------------------------- 50 | | Extra classes 51 | |-------------------------------------------------------------------------- 52 | | 53 | | These implementations are not really extended, but called with magic functions 54 | | 55 | */ 56 | 57 | 'extra' => [ 58 | 'Eloquent' => ['Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'], 59 | 'Session' => ['Illuminate\Session\Store'], 60 | ], 61 | 62 | 'magic' => [ 63 | 'Log' => [ 64 | 'debug' => 'Monolog\Logger::addDebug', 65 | 'info' => 'Monolog\Logger::addInfo', 66 | 'notice' => 'Monolog\Logger::addNotice', 67 | 'warning' => 'Monolog\Logger::addWarning', 68 | 'error' => 'Monolog\Logger::addError', 69 | 'critical' => 'Monolog\Logger::addCritical', 70 | 'alert' => 'Monolog\Logger::addAlert', 71 | 'emergency' => 'Monolog\Logger::addEmergency', 72 | ] 73 | ], 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Interface implementations 78 | |-------------------------------------------------------------------------- 79 | | 80 | | These interfaces will be replaced with the implementing class. Some interfaces 81 | | are detected by the helpers, others can be listed below. 82 | | 83 | */ 84 | 85 | 'interfaces' => [ 86 | '\Illuminate\Contracts\Auth\Authenticatable' => config('auth.model', 'App\User'), 87 | ], 88 | 89 | /* 90 | |-------------------------------------------------------------------------- 91 | | Support for custom DB types 92 | |-------------------------------------------------------------------------- 93 | | 94 | | This setting allow you to map any custom database type (that you may have 95 | | created using CREATE TYPE statement or imported using database plugin 96 | | / extension to a Doctrine type. 97 | | 98 | | Each key in this array is a name of the Doctrine2 DBAL Platform. Currently valid names are: 99 | | 'postgresql', 'db2', 'drizzle', 'mysql', 'oracle', 'sqlanywhere', 'sqlite', 'mssql' 100 | | 101 | | This name is returned by getName() method of the specific Doctrine/DBAL/Platforms/AbstractPlatform descendant 102 | | 103 | | The value of the array is an array of type mappings. Key is the name of the custom type, 104 | | (for example, "jsonb" from Postgres 9.4) and the value is the name of the corresponding Doctrine2 type (in 105 | | our case it is 'json_array'. Doctrine types are listed here: 106 | | http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html 107 | | 108 | | So to support jsonb in your models when working with Postgres, just add the following entry to the array below: 109 | | 110 | | "postgresql" => array( 111 | | "jsonb" => "json_array", 112 | | ), 113 | | 114 | */ 115 | 'custom_db_types' => [ 116 | 117 | ], 118 | 119 | ]; 120 | -------------------------------------------------------------------------------- /config/image.php: -------------------------------------------------------------------------------- 1 | 'gd' 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_DRIVER', 'smtp'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | SMTP Host Address 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may provide the host address of the SMTP server used by your 26 | | applications. A default option is provided that is compatible with 27 | | the Mailgun mail service which will provide reliable deliveries. 28 | | 29 | */ 30 | 31 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | SMTP Host Port 36 | |-------------------------------------------------------------------------- 37 | | 38 | | This is the SMTP port used by your application to deliver e-mails to 39 | | users of the application. Like the host we have set this value to 40 | | stay compatible with the Mailgun e-mail application by default. 41 | | 42 | */ 43 | 44 | 'port' => env('MAIL_PORT', 587), 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Global "From" Address 49 | |-------------------------------------------------------------------------- 50 | | 51 | | You may wish for all e-mails sent by your application to be sent from 52 | | the same address. Here, you may specify a name and address that is 53 | | used globally for all e-mails that are sent by your application. 54 | | 55 | */ 56 | 57 | 'from' => ['address' => env('MAIL_FROM', null), 'name' => env('SITE_NAME', null)], 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | E-Mail Encryption Protocol 62 | |-------------------------------------------------------------------------- 63 | | 64 | | Here you may specify the encryption protocol that should be used when 65 | | the application send e-mail messages. A sensible default using the 66 | | transport layer security protocol should provide great security. 67 | | 68 | */ 69 | 70 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | SMTP Server Username 75 | |-------------------------------------------------------------------------- 76 | | 77 | | If your SMTP server requires a username for authentication, you should 78 | | set it here. This will get used to authenticate with your server on 79 | | connection. You may also set the "password" value below this one. 80 | | 81 | */ 82 | 83 | 'username' => env('MAIL_USERNAME'), 84 | 85 | /* 86 | |-------------------------------------------------------------------------- 87 | | SMTP Server Password 88 | |-------------------------------------------------------------------------- 89 | | 90 | | Here you may set the password required by your SMTP server to send out 91 | | messages from your application. This will be given to the server on 92 | | connection so that the application will be able to send messages. 93 | | 94 | */ 95 | 96 | 'password' => env('MAIL_PASSWORD'), 97 | 98 | /* 99 | |-------------------------------------------------------------------------- 100 | | Sendmail System Path 101 | |-------------------------------------------------------------------------- 102 | | 103 | | When using the "sendmail" driver to send e-mails, we will need to know 104 | | the path to where Sendmail lives on this server. A default path has 105 | | been provided here, which will work well on most of your systems. 106 | | 107 | */ 108 | 109 | 'sendmail' => '/usr/sbin/sendmail -bs', 110 | 111 | /* 112 | |-------------------------------------------------------------------------- 113 | | Mail "Pretend" 114 | |-------------------------------------------------------------------------- 115 | | 116 | | When this option is enabled, e-mail will not actually be sent over the 117 | | web and will instead be written to your application's logs files so 118 | | you may inspect the message. This is great for local development. 119 | | 120 | */ 121 | 122 | 'pretend' => false, 123 | 124 | ]; 125 | -------------------------------------------------------------------------------- /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 | 'queue' => 'your-queue-url', 57 | 'region' => 'us-east-1', 58 | ], 59 | 60 | 'iron' => [ 61 | 'driver' => 'iron', 62 | 'host' => 'mq-aws-us-east-1.iron.io', 63 | 'token' => 'your-token', 64 | 'project' => 'your-project-id', 65 | 'queue' => 'your-queue-name', 66 | 'encrypt' => true, 67 | ], 68 | 69 | 'redis' => [ 70 | 'driver' => 'redis', 71 | 'connection' => 'default', 72 | 'queue' => 'default', 73 | 'expire' => 60, 74 | ], 75 | 76 | ], 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Failed Queue Jobs 81 | |-------------------------------------------------------------------------- 82 | | 83 | | These options configure the behavior of failed queue job logging so you 84 | | can control which database and table are used to store the jobs that 85 | | have failed. You may change them to any database / table you wish. 86 | | 87 | */ 88 | 89 | 'failed' => [ 90 | 'database' => 'mysql', 'table' => 'failed_jobs', 91 | ], 92 | 93 | ]; 94 | -------------------------------------------------------------------------------- /config/recaptcha.php: -------------------------------------------------------------------------------- 1 | env('RECAPTCHA_PUBLIC_KEY', ''), 17 | 'private_key' => env('RECAPTCHA_PRIVATE_KEY', ''), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Template 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Set a template to use if you don't want to use the standard one. 25 | | 26 | */ 27 | 'template' => '', 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Driver 32 | |-------------------------------------------------------------------------- 33 | | 34 | | Determine how to call out to get response; values are 'curl' or 'native'. 35 | | Only applies to v2. 36 | | 37 | */ 38 | 'driver' => 'curl', 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Options 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Various options for the driver 46 | | 47 | */ 48 | 'options' => [ 49 | 50 | 'curl_timeout' => 1, 51 | 52 | ], 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Version 57 | |-------------------------------------------------------------------------- 58 | | 59 | | Set which version of ReCaptcha to use. 60 | | 61 | */ 62 | 63 | 'version' => 2, 64 | 65 | ]; 66 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => '', 19 | 'secret' => '', 20 | ], 21 | 22 | 'mandrill' => [ 23 | 'secret' => '', 24 | ], 25 | 26 | 'ses' => [ 27 | 'key' => '', 28 | 'secret' => '', 29 | 'region' => 'us-east-1', 30 | ], 31 | 32 | 'stripe' => [ 33 | 'model' => App\Models\User::class, 34 | 'key' => '', 35 | 'secret' => '', 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'database'), 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' => true, 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' => 'upste_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' => env('COOKIE_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' => env('SECURE_COOKIES', true), 152 | 153 | ]; 154 | -------------------------------------------------------------------------------- /config/upste.php: -------------------------------------------------------------------------------- 1 | env('SITE_NAME', 'uPste'), 5 | 'upload_url' => str_finish(env('UPLOAD_URL', 'https://a.example.com'), '/'), 6 | 'upload_domain' => parse_url(env('UPLOAD_URL', 'https://a.example.com'), PHP_URL_HOST), 7 | 'upload_slug_length' => env('UPLOAD_SLUG_LENGTH', 3), 8 | 'owner_name' => env('OWNER_NAME', 'Me'), 9 | 'owner_email' => env('OWNER_EMAIL', 'me@example.com'), 10 | 'owner_gpg' => env('OWNER_GPG', null), 11 | 'upload_limit' => env('PER_UPLOAD_LIMIT', 20) * 1000000, 12 | 'irc_channel' => env('IRC_CHANNEL', null), 13 | 'irc_server' => env('IRC_SERVER', null), 14 | 'strip_exif' => env('STRIP_EXIF', true), 15 | 'user_storage_quota' => env('USER_STORAGE_QUOTA', 0) * 1000000, // Megabytes to bytes 16 | 'recaptcha_enabled' => env('RECAPTCHA_PUBLIC_KEY', null) && env('RECAPTCHA_PUBLIC_KEY', null), 17 | 'sendfile_method' => env('SENDFILE_METHOD', null), 18 | 'require_user_approval' => env('REQUIRE_USER_APPROVAL', true), 19 | 'password_hash_rounds' => env('PASSWORD_HASH_ROUNDS', 10), 20 | 'require_email_verification' => env('REQUIRE_EMAIL_VERIFICATION', false), 21 | ]; 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | define(App\User::class, function ($faker) { 15 | return [ 16 | 'name' => $faker->name, 17 | 'email' => $faker->email, 18 | 'password' => str_random(10), 19 | 'remember_token' => str_random(10), 20 | ]; 21 | }); 22 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/database/migrations/.gitkeep -------------------------------------------------------------------------------- /database/migrations/2015_12_09_172957_create_users_table.php: -------------------------------------------------------------------------------- 1 | engine = 'InnoDB'; 17 | $table->increments('id'); 18 | $table->string('name')->unique(); 19 | $table->string('email')->unique(); 20 | $table->string('apikey', 64)->unique(); 21 | $table->string('password', 60); 22 | $table->boolean('admin')->default(false); 23 | $table->boolean('enabled')->default(false); 24 | $table->boolean('banned')->default(false); 25 | $table->rememberToken(); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::drop('users'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/migrations/2015_12_09_173005_create_uploads_table.php: -------------------------------------------------------------------------------- 1 | engine = 'InnoDB'; 17 | $table->increments('id'); 18 | $table->integer('user_id')->unsigned(); 19 | $table->string('hash', 20); 20 | $table->string('name', 32)->unique(); 21 | $table->unsignedBigInteger('size'); 22 | $table->string('original_name'); 23 | $table->timestamps(); 24 | $table->foreign('user_id')->references('id')->on('users')->onDelete('CASCADE'); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::drop('uploads'); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /database/migrations/2015_12_09_173022_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | engine = 'InnoDB'; 17 | $table->string('email')->index(); 18 | $table->string('token')->index(); 19 | $table->timestamp('created_at'); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::drop('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2015_12_09_235053_fix_hash_length_for_uploads_table.php: -------------------------------------------------------------------------------- 1 | string('hash', 40)->change(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('uploads', function (Blueprint $table) { 28 | $table->string('hash', 20)->change(); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2015_12_11_221322_create_sessions_table.php: -------------------------------------------------------------------------------- 1 | string('id')->unique(); 17 | $table->text('payload'); 18 | $table->integer('last_activity'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::drop('sessions'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2015_12_15_101257_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 17 | $table->string('queue'); 18 | $table->longText('payload'); 19 | $table->tinyInteger('attempts')->unsigned(); 20 | $table->tinyInteger('reserved')->unsigned(); 21 | $table->unsignedInteger('reserved_at')->nullable(); 22 | $table->unsignedInteger('available_at'); 23 | $table->unsignedInteger('created_at'); 24 | $table->index(['queue', 'reserved', 'reserved_at']); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::drop('jobs'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2015_12_15_101552_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 17 | $table->text('connection'); 18 | $table->text('queue'); 19 | $table->longText('payload'); 20 | $table->timestamp('failed_at'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::drop('failed_jobs'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2016_02_20_173427_update_session_drivers.php: -------------------------------------------------------------------------------- 1 | integer('user_id')->nullable(); 17 | $table->string('ip_address')->nullable(); 18 | $table->text('user_agent'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table('sessions', function (Blueprint $table) { 30 | $table->dropColumn(['user_id', 'ip_address', 'user_agent']); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2016_03_20_131515_add_original_hash.php: -------------------------------------------------------------------------------- 1 | string('original_hash', 40); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('uploads', function (Blueprint $table) { 28 | $table->dropColumn('original_hash'); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2016_03_30_213843_increase_password_length.php: -------------------------------------------------------------------------------- 1 | string('password', 255)->change(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('users', function (Blueprint $table) { 28 | $table->string('password', 60)->change(); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2016_04_01_022122_add_view_counter.php: -------------------------------------------------------------------------------- 1 | unsignedBigInteger('downloads')->default(0); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('uploads', function (Blueprint $table) { 28 | $table->dropColumn('downloads'); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2016_04_01_025729_rename_download.php: -------------------------------------------------------------------------------- 1 | renameColumn('downloads', 'views'); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('uploads', function (Blueprint $table) { 28 | $table->renameColumn('views', 'downloads'); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2016_04_03_195138_create_preferences_table.php: -------------------------------------------------------------------------------- 1 | engine = 'InnoDB'; 17 | $table->integer('user_id')->unsigned(); 18 | $table->string('timezone')->default('UTC'); 19 | $table->integer('pagination_items')->default(9); 20 | $table->timestamps(); 21 | $table->foreign('user_id')->references('id')->on('users')->onDelete('CASCADE'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::drop('preferences'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2016_04_03_213046_update_default_pagination.php: -------------------------------------------------------------------------------- 1 | integer('pagination_items')->default(6)->change(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('preferences', function (Blueprint $table) { 28 | $table->integer('pagination_items')->default(9)->change(); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2016_07_26_153539_add_email_confirmation.php: -------------------------------------------------------------------------------- 1 | boolean('confirmed')->default(false); 17 | $table->string('confirmation_code', 32)->nullable(); 18 | }); 19 | // For existing users. 20 | DB::table('users')->update(['confirmed' => true]); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::table('users', function (Blueprint $table) { 31 | $table->dropColumn('confirmation_code', 'confirmed'); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/seeds/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/database/seeds/.gitkeep -------------------------------------------------------------------------------- /database/seeds/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call(UserTableSeeder::class); 18 | 19 | Model::reguard(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Elixir Asset Management 4 | |-------------------------------------------------------------------------- 5 | | 6 | | Elixir provides a clean, fluent API for defining some basic Gulp tasks 7 | | for your Laravel application. By default, we are compiling the Sass 8 | | file for our application, as well as publishing vendor resources. 9 | | 10 | */ 11 | 12 | // Disable notify-send 13 | process.env.DISABLE_NOTIFIER = true; 14 | 15 | var elixir = require('laravel-elixir'); 16 | elixir.config.sourcemaps = false; 17 | 18 | elixir(function(mix) { 19 | mix.sass([ 20 | 'global.scss' 21 | ], 'public/assets/css/global.css'); 22 | 23 | mix.sass([ 24 | 'error.scss' 25 | ], 'public/assets/css/error.css'); 26 | 27 | mix.sass('thumbnailhover.scss', 'public/assets/css/thumbnailhover.css'); 28 | 29 | mix.styles([ 30 | '../../../node_modules/dropzone/dist/basic.css' 31 | ], 'public/assets/css/dropzone.css'); 32 | 33 | mix.scripts('thumbnailhover.js', 'public/assets/js/thumbnailhover.js'); 34 | 35 | mix.scripts([ 36 | '../../../node_modules/dropzone/dist/dropzone.js', 37 | 'dropzone.js' 38 | ], 'public/assets/js/dropzone.js'); 39 | 40 | mix.scripts([ 41 | '../../../node_modules/jquery/dist/jquery.js', 42 | '../../../node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js', 43 | '../../../node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js', 44 | '../../../node_modules/bootstrap-sass/assets/javascripts/bootstrap/collapse.js', 45 | '../../../node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js', 46 | '../../../node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js', 47 | 'global.js' 48 | ], 'public/assets/js/global.js'); 49 | 50 | mix.version([ 51 | 'assets/css/global.css', 52 | 'assets/css/error.css', 53 | 'assets/js/thumbnailhover.js', 54 | 'assets/css/thumbnailhover.css', 55 | 'assets/css/dropzone.css', 56 | 'assets/js/global.js', 57 | 'assets/js/dropzone.js', 58 | 'assets/img/favicon.png', 59 | 'assets/img/thumbnail.png' 60 | ]); 61 | }); 62 | -------------------------------------------------------------------------------- /nginx.conf.example: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name u.localhost; 4 | root /path/to/pste/public; 5 | index index.php; 6 | 7 | client_max_body_size 100M; 8 | 9 | location ~ /index\.php$ { 10 | include fastcgi_params; 11 | try_files $uri =404; 12 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 13 | fastcgi_pass unix:/var/run/php7-fpm.sock; 14 | fastcgi_index index.php; 15 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 16 | } 17 | 18 | location / { 19 | try_files $uri $uri/ /index.php?$query_string; 20 | } 21 | 22 | location ~ /robots.txt { 23 | allow all; 24 | log_not_found off; 25 | access_log off; 26 | } 27 | } 28 | 29 | server { 30 | listen 80; 31 | server_name a.localhost; 32 | root /path/to/pste/public; 33 | index index.php; 34 | 35 | include mime.types; 36 | 37 | # Very important to at least serve .php files as plaintext, otherwise your site WILL be hacked in no time at all. 38 | # SVG served as plaintext due to https://hackerone.com/reports/148853 39 | types { 40 | text/plain txt ini html htm shtml sh desktop; 41 | text/plain pl go py cs c java fish php svg js; 42 | text/plain rb rs lua ls hy asm S conf vim; 43 | text/plain moon log tcl tk md coffee; 44 | text/plain scss ts less d hs; 45 | } 46 | 47 | gzip on; 48 | expires modified +1h; 49 | 50 | # It is very important to be specific here, otherwise your users won't be able to upload PHP files to be served as plaintext. 51 | location ~ /index\.php$ { 52 | include fastcgi_params; 53 | try_files $uri =404; 54 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 55 | fastcgi_pass unix:/var/run/php7-fpm.sock; 56 | fastcgi_index index.php; 57 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 58 | } 59 | 60 | location / { 61 | try_files $uri $uri/ /index.php?$query_string; 62 | } 63 | 64 | location = / { 65 | return 302 http://u.localhost; 66 | } 67 | 68 | location /uploads { 69 | internal; 70 | alias /path/to/pste/storage/app/uploads; 71 | } 72 | 73 | location ~ /robots.txt { 74 | allow all; 75 | log_not_found off; 76 | access_log off; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "gulp": "^3.8.8" 5 | }, 6 | "dependencies": { 7 | "laravel-elixir": "^5.0.0", 8 | "bootstrap-sass": "^3.0.0", 9 | "jquery": "1.12.2", 10 | "dropzone": "4.3.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | suites: 2 | main: 3 | namespace: App 4 | psr4_prefix: App 5 | src_path: app -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | app/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/assets/css/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /public/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/public/assets/img/favicon.png -------------------------------------------------------------------------------- /public/assets/img/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/public/assets/img/thumbnail.png -------------------------------------------------------------------------------- /public/assets/js/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/public/favicon.ico -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /a 3 | Disallow: /u 4 | -------------------------------------------------------------------------------- /public/vendor/folklore/image/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/public/vendor/folklore/image/.gitkeep -------------------------------------------------------------------------------- /public/vendor/folklore/image/js/image.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | // AMD. Register as an anonymous module. 4 | define([], factory); 5 | } else if (typeof exports === 'object') { 6 | // Node. Does not work with strict CommonJS, but 7 | // only CommonJS-like environments that support module.exports, 8 | // like Node. 9 | module.exports = factory(); 10 | } else { 11 | // Browser globals (root is window) 12 | if(typeof(root.Folklore) === 'undefined') { 13 | root.Folklore = {}; 14 | } 15 | root.Folklore.Image = factory(); 16 | } 17 | }(this, function () { 18 | 19 | 'use strict'; 20 | 21 | var URL_PARAMETER = '-image({options})'; 22 | 23 | // Build a image formatted URL 24 | function url(src, width, height, options) { 25 | 26 | // Don't allow empty strings 27 | if (!src || !src.length) return; 28 | 29 | //If width parameter is an array, use it as options 30 | if(width instanceof Object) 31 | { 32 | options = width; 33 | width = null; 34 | height = null; 35 | } 36 | 37 | //Get size 38 | if (!width) width = '_'; 39 | if (!height) height = '_'; 40 | 41 | // Produce the image option 42 | var params = []; 43 | 44 | //Add size if presents 45 | if(width != '_' || height != '_') { 46 | params.push(width+'x'+height); 47 | } 48 | 49 | // Add options. 50 | if (options && options instanceof Object) { 51 | var val, key; 52 | for (key in options) { 53 | val = options[key]; 54 | if (val === true || val === null) { 55 | params.push(key); 56 | } 57 | else if (val instanceof Array) { 58 | params.push(key+'('+val.join(',')+')'); 59 | } 60 | else { 61 | params.push(key+'('+val+')'); 62 | } 63 | } 64 | } 65 | 66 | params = params.join('-'); 67 | var parameter = URL_PARAMETER.replace('{options}',params); 68 | 69 | // Break the path apart and put back together again 70 | return src.replace(/^(.+)(\.[a-z]+)$/i, "$1"+parameter+"$2"); 71 | 72 | } 73 | 74 | // Expose public methods. 75 | return { 76 | url: url 77 | }; 78 | })); 79 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | uPste 2 | ========== 3 | 4 | # Unmaintained, see [FoxDev/pste](https://github.com/FoxDev/pste) for the rewrite. 5 | 6 | ![Language](https://img.shields.io/badge/language-PHP-blue.svg?style=flat-square) 7 | [![License](https://img.shields.io/badge/license-AGPLv3-blue.svg?style=flat-square)](https://www.gnu.org/licenses/agpl-3.0.en.html) 8 | [![IRC](https://img.shields.io/badge/chat-IRC-green.svg?style=flat-square)](https://qchat.rizon.net/?channels=leliana) 9 | [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=TheReverend403&url=https://github.com/TheReverend403/uPste&title=uPste&language=&tags=github&category=software) 10 | 11 | uPste is a file hosting application with an emphasis on serving technology communities. 12 | 13 | It is released under the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html). 14 | 15 | The official demo of this project is available at https://u.pste.pw. 16 | 17 | Interested in contributing, want some help or just have some questions? Join us on [irc.rizon.net in #leliana](https://qchat.rizon.net/?channels=leliana) 18 | 19 | # Screenshots 20 | 21 | ![Index Preview](https://a.pste.pw/gsA.png) 22 | ![User Preview](https://a.pste.pw/5ZJ.png) 23 | ![Uploads Preview](https://a.pste.pw/Mz1.png) 24 | 25 | # Requirements 26 | 27 | * The PHP GD extension. 28 | * The ability to install [Laravel](http://laravel.com/docs/5.2/installation). 29 | * Any database supported by [Eloquent](http://laravel.com/docs/5.2/database#configuration). 30 | * [Laravel Elixir](https://laravel.com/docs/5.2/elixir#installation). 31 | * A little bit of command line experience. 32 | 33 | # Installation 34 | 35 | We'll assume you already have a database, setting that up is beyond the scope of this readme. 36 | 37 | ````bash 38 | git clone https://github.com/TheReverend403/uPste 39 | cd uPste 40 | composer install # Installs laravel, all dependencies, npm dependencies, compiles assets and generates your app key. 41 | ```` 42 | 43 | Open .env in the root directory of uPste and edit the settings within to suit your site. Make sure to read what each one does, and feel free to ask if you're not sure. 44 | 45 | ````bash 46 | php artisan migrate # Creates your database schema 47 | ```` 48 | 49 | The next, and last part is entirely dependent you and how you want to configure your webserver, 50 | but you're basically going to want two domains (or subdomains). 51 | 52 | There is an example nginx config in the root of this repository. 53 | Feel free to adapt it to your server software, but you MUST keep the /uploads location intact for x-accel/x-sendfile to work if you intend to use them. 54 | 55 | That's it, you're done! Now just navigate to your site and register. 56 | The first user registered will be automatically enabled and made an admin, so take measures to make sure this is you. 57 | 58 | # Upgrading 59 | 60 | **WARNING:** It is strongly recommended you check the release notes for each new version to see if there are any breaking changes that affect you. 61 | 62 | Upgrading is easy. Just run the following commands, and make sure to check .env.example for any new config options you need to set. 63 | 64 | ````bash 65 | cd /path/to/uPste 66 | git pull 67 | composer update 68 | ```` 69 | 70 | If you change .env values and they don't seem to be doing anything, try running `composer recache` to rebuild the site caches, including the config cache. 71 | -------------------------------------------------------------------------------- /resources/assets/js/dropzone.js: -------------------------------------------------------------------------------- 1 | Dropzone.autoDiscover = false; 2 | $(function () { 3 | $(document.body).dropzone({ 4 | url: window.api_upload_url, 5 | previewsContainer: "#previews", 6 | clickable: "#upload-button", 7 | maxFilesize: window.max_file_size, 8 | params: {'key': window.api_key}, 9 | init: function () { 10 | this.on("success", function (file, responseText) { 11 | console.log(responseText); 12 | $(file.previewTemplate).append($('', { 13 | 'href': responseText.url, 14 | html: responseText.url 15 | })) 16 | }).on("addedfile", function (file) { 17 | if (!file.type.match(/image.*/)) { 18 | this.emit("thumbnail", file, window.dropzone_thumbnail); 19 | } 20 | }) 21 | } 22 | }) 23 | }); 24 | -------------------------------------------------------------------------------- /resources/assets/js/global.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#flash-overlay-modal').modal(); 3 | $('div.alert').not('.alert-important').delay(3000).slideUp(); 4 | $('form[confirm]').on('submit', function(){ 5 | return window.confirm(this.getAttribute('confirm')); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /resources/assets/js/thumbnailhover.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Image preview script 3 | * powered by jQuery (http://www.jquery.com) 4 | * 5 | * written by Alen Grakalic (http://cssglobe.com) 6 | * 7 | * for more info visit http://cssglobe.com/post/1695/easiest-tooltip-and-image-preview-using-jquery 8 | * 9 | */ 10 | 11 | // https://stackoverflow.com/questions/16289159/how-to-show-image-preview-on-thumbnail-hover 12 | 13 | this.imagePreview = function () { 14 | 15 | var xOffset = 15; 16 | var yOffset = 30; 17 | 18 | var Mx = $(document).width(); 19 | var My = $(document).height(); 20 | 21 | var callback = function (event) { 22 | var $img = $("#preview"); 23 | 24 | var trc_x = xOffset + $img.width(); 25 | var trc_y = yOffset + $img.height(); 26 | 27 | trc_x = Math.min(trc_x + event.pageX, Mx); 28 | trc_y = Math.min(trc_y + event.pageY, My); 29 | 30 | $img.css("top", (trc_y - $img.height()) + "px") 31 | .css("left", (trc_x - $img.width()) + "px") 32 | .css("z-index", 1000); 33 | }; 34 | 35 | $("a.preview").hover(function (e) { 36 | Mx = $(document).width(); 37 | My = $(document).height(); 38 | 39 | this.t = this.title; 40 | this.title = ""; 41 | var c = (this.t != "") ? "
" + this.t : ""; 42 | $("body").append("

Preview" + c + "

"); 43 | callback(e); 44 | $("#preview").fadeIn("fast"); 45 | }, 46 | function () { 47 | this.title = this.t; 48 | $("#preview").remove(); 49 | }).mousemove(callback); 50 | }; 51 | 52 | $(document).ready(function () { 53 | imagePreview(); 54 | }); -------------------------------------------------------------------------------- /resources/assets/sass/.gitignore: -------------------------------------------------------------------------------- 1 | *.map 2 | *.css -------------------------------------------------------------------------------- /resources/assets/sass/error.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lato:100'); 2 | 3 | html, body { 4 | height: 100%; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | padding: 0; 10 | width: 100%; 11 | font-weight: 100; 12 | font-family: 'Lato', sans-serif; 13 | font-size: 68px; 14 | } 15 | 16 | .container { 17 | position: absolute; 18 | top: 50%; 19 | left: 50%; 20 | transform: translate(-50%, -50%); 21 | display: inline; 22 | text-align: center; 23 | margin-left: auto; 24 | margin-right: auto; 25 | } 26 | 27 | .content { 28 | text-align: center; 29 | } -------------------------------------------------------------------------------- /resources/assets/sass/global.scss: -------------------------------------------------------------------------------- 1 | @import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; 2 | 3 | .upload { 4 | margin-bottom: 20px; 5 | margin-top: 20px; 6 | 7 | #upload-button { 8 | background: rgba(202, 230, 190, 0.75); 9 | border: 1px solid #B7D1A0; 10 | border-radius: 4px; 11 | width: 100%; 12 | text-align: center; 13 | color: #468847; 14 | cursor: pointer; 15 | display: inline-block; 16 | font-size: 24px; 17 | padding: 28px 48px; 18 | text-shadow: 0 1px rgba(255, 255, 255, 0.5); 19 | transition: background-color 0.25s, width 0.5s, height 0.5s; 20 | } 21 | } 22 | 23 | .container-sm { 24 | max-width: 700px; 25 | margin-left: auto; 26 | margin-right: auto; 27 | } 28 | 29 | .message-area { 30 | max-width: 700px; 31 | margin-left: auto; 32 | margin-right: auto; 33 | text-align: center; 34 | } 35 | 36 | ul, ol .list-noborder { 37 | margin: 0; 38 | } 39 | 40 | tr td:last-child { 41 | width: 1%; 42 | white-space: nowrap; 43 | } 44 | 45 | .panel { 46 | &-no-margin { 47 | margin-bottom: 0; 48 | } 49 | } 50 | 51 | .img-thumbnail { 52 | max-width: none; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /resources/assets/sass/thumbnailhover.scss: -------------------------------------------------------------------------------- 1 | #preview { 2 | position: absolute; 3 | border: 1px solid #ccc; 4 | background: #333; 5 | padding: 5px; 6 | display: none; 7 | color: #fff; 8 | 9 | img { 10 | max-width: 768px; 11 | max-height: 768px; 12 | } 13 | } -------------------------------------------------------------------------------- /resources/lang/en/messages.php: -------------------------------------------------------------------------------- 1 | 'Deleted :name.', 5 | 'api_key_changed' => 'Your API key was reset. New API key: :api_key', 6 | 'invalid_file_upload' => 'The file you\'re trying to upload isn\'t a valid form upload.', 7 | 'upload_file_not_found' => 'You didn\'t supply a file to upload.', 8 | 'upload_quota_reached' => 'You have reached the per-user storage quota of :limit.', 9 | 'upload_too_large' => 'This file exceeds the max upload size of :limit.', 10 | 'no_uploads_found' => 'You do not currently have anything uploaded.', 11 | 'could_not_write_image' => 'Error occurred while saving file.', 12 | 'could_not_read_image' => 'Error occurred while reading file.', 13 | 'unsupported_image' => 'Unsupported image type.', 14 | 'preferences_saved' => 'Preferences updated!', 15 | 'banned' => 'You have been banned. Contact an admin if you believe that this is an error.', 16 | 'not_logged_in' => 'You must log in to access this page.', 17 | 'not_activated' => 'Your account has not been approved yet. You will be notified via email when your account status changes.', 18 | 'activation_pending' => 'Your account request has been successfully registered. You will receive a notification email at :email when an admin accepts or rejects your request.', 19 | 'email_confirmed' => 'Your email has been confirmed, and you have been automatically logged in.', 20 | 'confirmation_pending' => 'Please check your email for a confirmation code from ' . config('mail.from.address'), 21 | 'not_confirmed' => 'Your account has not been confirmed. Please check your email for a confirmation code from ' . config('mail.from.address'), 22 | 'no_such_confirmation_code' => 'We couldn\'t find a pending registration with that confirmation code.', 23 | 'delete_all_uploads' => 'Delete all uploads', 24 | 'confirm_deletion' => 'Are you sure that you want to delete all of your uploads?', 25 | 'all_uploads_deleted' => 'All of your uploads have been deleted.', 26 | 'admin' => [ 27 | 'no_uploads_found' => ':name has no uploads.', 28 | 'unbanned_user' => 'Unbanned :name.', 29 | 'banned_user' => 'Banned :name.', 30 | 'deleted_user' => 'Deleted :name.', 31 | 'account_accepted' => 'Accepted :name', 32 | 'account_rejected' => 'Rejected :name.', 33 | 'failed_superuser_action' => 'You cannot :type the superuser account!', 34 | ], 35 | ]; 36 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Password must be at least six characters long and match the confirmation.', 17 | 'user' => "We can't find an account with that e-mail address.", 18 | 'token' => 'This password reset token is invalid.', 19 | 'sent' => 'We have emailed your password reset link!', 20 | 'reset' => 'Your password has been reset!', 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /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 | 'email' => 'The :attribute must be a valid email address.', 38 | 'filled' => 'The :attribute field is required.', 39 | 'exists' => 'The selected :attribute is invalid.', 40 | 'image' => 'The :attribute must be an image.', 41 | 'in' => 'The selected :attribute is invalid.', 42 | 'integer' => 'The :attribute must be an integer.', 43 | 'ip' => 'The :attribute must be a valid IP address.', 44 | 'max' => [ 45 | 'numeric' => 'The :attribute may not be greater than :max.', 46 | 'file' => 'The :attribute may not be greater than :max kilobytes.', 47 | 'string' => 'The :attribute may not be greater than :max characters.', 48 | 'array' => 'The :attribute may not have more than :max items.', 49 | ], 50 | 'mimes' => 'The :attribute must be a file of type: :values.', 51 | 'min' => [ 52 | 'numeric' => 'The :attribute must be at least :min.', 53 | 'file' => 'The :attribute must be at least :min kilobytes.', 54 | 'string' => 'The :attribute must be at least :min characters.', 55 | 'array' => 'The :attribute must have at least :min items.', 56 | ], 57 | 'not_in' => 'The selected :attribute is invalid.', 58 | 'numeric' => 'The :attribute must be a number.', 59 | 'recaptcha' => 'A valid captcha is required.', 60 | 'regex' => 'The :attribute format is invalid.', 61 | 'required' => 'The :attribute field is required.', 62 | 'required_if' => 'The :attribute field is required when :other is :value.', 63 | 'required_with' => 'The :attribute field is required when :values is present.', 64 | 'required_with_all' => 'The :attribute field is required when :values is present.', 65 | 'required_without' => 'The :attribute field is required when :values is not present.', 66 | 'required_without_all' => 'The :attribute field is required when none of :values are present.', 67 | 'same' => 'The :attribute and :other must match.', 68 | 'size' => [ 69 | 'numeric' => 'The :attribute must be :size.', 70 | 'file' => 'The :attribute must be :size kilobytes.', 71 | 'string' => 'The :attribute must be :size characters.', 72 | 'array' => 'The :attribute must contain :size items.', 73 | ], 74 | 'string' => 'The :attribute must be a string.', 75 | 'timezone' => 'The :attribute must be a valid zone.', 76 | 'unique' => 'The :attribute has already been taken.', 77 | 'url' => 'The :attribute format is invalid.', 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Custom Validation Language Lines 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Here you may specify custom validation messages for attributes using the 85 | | convention "attribute.rule" to name the lines. This makes it quick to 86 | | specify a specific custom language line for a given attribute rule. 87 | | 88 | */ 89 | 90 | 'custom' => [ 91 | 'attribute-name' => [ 92 | 'rule-name' => 'custom-message', 93 | ], 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Custom Validation Attributes 99 | |-------------------------------------------------------------------------- 100 | | 101 | | The following language lines are used to swap attribute place-holders 102 | | with something more reader friendly such as E-Mail Address instead 103 | | of "email". This simply helps us make messages a little cleaner. 104 | | 105 | */ 106 | 107 | 'attributes' => [], 108 | 109 | ]; 110 | -------------------------------------------------------------------------------- /resources/views/account/api.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

Your API key is:

3 |
{{ Auth::user()->apikey }}
4 |

This key allows anyone to upload to {{ config('upste.site_name') }} as you. Do not let anyone else see it.

5 |
6 |
7 | 8 |
9 | {!! csrf_field() !!} 10 |
11 |
12 |

API Methods

13 |

All API methods require your API key as a parameter named key, either as a form field for POSTs or a GET parameter for GET requests.

14 |
15 |

POST {{ route('api.upload') }}

16 |

Upload a file to your account and get a link to the file for sharing.

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
ParameterDescriptionRequired
fileThe file you wish to upload.Yes
33 |

Example

34 |
curl \
35 | -F key={{ Auth::user()->apikey }} \
36 | -F file=@example.png \
37 | {{ route('api.upload') }}
38 | -------------------------------------------------------------------------------- /resources/views/account/faq.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.account') 2 | 3 | @section('title', 'FAQ') 4 | 5 | @section('content') 6 |
7 | @if (config('upste.strip_exif')) 8 |
9 |
10 | Why do my images have a different size and hash after I upload them? 11 |
12 |
13 |

We strip EXIF data from uploaded images to prevent accidental breaches of user privacy from EXIF tags such as geolocation. This results in a different file size, and obviously a different hash.

14 |
15 |
16 | @endif 17 | 18 | @if(!config('upste.user_storage_quota')) 19 |
20 |
21 | How much storage do I have? 22 |
23 |
24 |
25 |

"Don't take the piss" much.

26 |
oneesama
27 |
28 |
29 |
30 | @endif 31 | 32 |
33 |
34 | I found a bug or error, where do I report it? 35 |
36 |
39 |
40 | 41 |
42 |
43 | I found a security flaw, where do I report it? 44 |
45 |
46 |

Email 47 | {{ config('upste.owner_email') }}{!! config('upste.owner_gpg') ? sprintf(' (GPG)', config('upste.owner_gpg')) : '' !!}. 48 |

49 |

Please do not attempt to abuse bugs in the site's security for any purpose beyond reporting the bug as you will be instantly banned for life. Be responsible when other people's security is at risk. Don't be that guy. Nobody likes that guy.

50 |
51 |
52 |
53 | @stop -------------------------------------------------------------------------------- /resources/views/account/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.account') 2 | 3 | @section('title', 'My Account') 4 | 5 | @section('stylesheets') 6 | 7 | @stop 8 | 9 | @section('content') 10 | 17 |
18 |
19 |
20 |

Welcome to {{ config('upste.site_name') }}, {{ Auth::user()->name }}. Here's everything you need to 21 | know.

22 |
23 |
24 |
25 |
    26 |
  • For support, email {{ config('upste.owner_email') }}.
  • 27 | @if (config('upste.irc_server') && config('upste.irc_channel')) 28 |
  • For support or social, hang out in {{ config('upste.irc_channel') }} on {{ config('upste.irc_server') }}.
  • 29 | @endif 30 |
  • Max file size per upload is {{ App\Helpers::formatBytes(config('upste.upload_limit')) }}.
  • 31 |
  • Do not upload child porn or malware, you'll be banned without mercy.
  • 32 |
  • Scripts and third-party integrations are provided for members, 33 | {{ route('account.resources') }}. 34 |
  • 35 |
36 |
37 |
38 |
39 |
40 |

WARNING

41 |
42 |
43 |

While only members can upload files, all uploads, as well as their original names, are visible to the public if they know (or accidentally 44 | find) the URL. Therefore, DO NOT upload anything you consider private as we will not accept any 45 | responsibility if it gets leaked. If you must upload private files, consider encrypting them first. 46 |

47 |

tl;dr All uploads should be considered public.

48 |
49 |
50 |
51 |
52 |
53 |
54 | Drag and drop or click to upload files 55 |
56 | 59 |
60 |
61 | @include('account.api') 62 |
63 |
64 | @stop 65 | 66 | @section('javascript') 67 | 68 | @stop 69 | -------------------------------------------------------------------------------- /resources/views/account/preferences.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.account') 2 | 3 | @section('title', 'My Preferences') 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 10 | 19 |
20 | 21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 | {{ csrf_field() }} 31 |
32 |
33 | @endsection -------------------------------------------------------------------------------- /resources/views/account/resources.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.account') 2 | 3 | @section('title', 'My Resources') 4 | 5 | @section('content') 6 |
7 |
8 |

Third Party Integrations

9 |

If you make something neat, let us know and we'll feature it 10 | here.

11 |

Bash script

12 |

You can upload to {{ config('upste.site_name') }} with our bash script.

13 |

This script depends on slop and maim.

15 |

Save this to ~/.config/pstepw:

16 |
17 |
18 | #!/bin/bash
19 | 
20 | # DO NOT SHARE YOUR KEY
21 | key={{ Auth::user()->apikey }}
22 | # Log URLs
23 | log=false
24 | # Log file location (if relevant)
25 | logfile="$HOME/.pstepw"
26 | # Copy links to clipboard after upload (requires xclip)
27 | clipboard=true
28 | # Send a notification when done
29 | notify=true
30 | # Open URL in browser
31 | browser=true
32 | 
33 |
34 |

Save this to any location in your $PATH.

35 |

(plaintext version)

36 |
37 |
@include('account.resources.bash')
38 |
39 | @stop -------------------------------------------------------------------------------- /resources/views/account/resources/bash.blade.php: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # pstepw - Upload files/scrots/urls to {{ route('index') }} 4 | # By onodera, modified by TheReverend403 5 | 6 | ## CONFIGURATION 7 | source ~/.config/pstepw 8 | 9 | ## FUNCTIONS 10 | 11 | # This function sets $file to a selection scrot 12 | selection() { 13 | uploadme="/tmp/scrot.png" 14 | 15 | maim --hidecursor -s -b 2 -c 1,0,0,0.6 "$uploadme" 2> "/dev/null" 16 | if [[ "$?" -ge 1 ]]; then 17 | echo "Selection cancelled." 18 | exit 1 19 | fi 20 | 21 | word=selection 22 | } 23 | 24 | # This function sets $file to your clipboard contents 25 | clipboard() { 26 | uploadme="/tmp/scrot.txt" 27 | 28 | xclip -o > "$uploadme" 29 | 30 | word=clipboard 31 | } 32 | 33 | # This function sets $file an url 34 | url() { 35 | type="$(echo "$location" | rev | cut -d "." -f 1 | rev)" 36 | uploadme="/tmp/url.$type" 37 | 38 | wget --quiet "$location" -O "$uploadme" 39 | 40 | word=url 41 | } 42 | 43 | # This function sets $file a file 44 | file() { 45 | if [[ -f "$location" ]]; then 46 | uploadme="$location" 47 | else 48 | echo "File not found." 49 | exit 1 50 | fi 51 | 52 | word=file 53 | } 54 | 55 | # This function sets $file to a full screen scrot 56 | desktop() { 57 | uploadme="/tmp/scrot.png" 58 | 59 | maim --hidecursor "$uploadme" 60 | 61 | word=desktop 62 | } 63 | 64 | # This function uploads the $file 65 | upload() { 66 | url="$(curl --silent -F key="$key" -F file="@$uploadme" "{{ route('api.upload') }}" | grep -o -i "{{ config('upste.upload_url') }}*.[a-z0-9._-]*")" 67 | } 68 | 69 | # This function logs the url, copies the url to the clipboard, and/or opens the url in your browser 70 | feedback() { 71 | # Copy url to clipboard 72 | if [[ "$clipboard" == true ]]; then 73 | echo "$url" | xclip -selection primary 74 | echo "$url" | xclip -selection clipboard 75 | fi 76 | 77 | # Log url 78 | if [[ "$log" == true ]]; then 79 | echo "$url" >> "$logfile" 80 | fi 81 | 82 | # Open url in browser 83 | if [[ "$browser" == true ]]; then 84 | xdg-open "$url" 85 | fi 86 | 87 | # Send notification 88 | if [[ "$notify" == true ]]; then 89 | notify-send "Upload complete: $url" 90 | fi 91 | 92 | echo "$url" 93 | } 94 | 95 | ## EXECUTE 96 | 97 | if [[ "$#" -ge 1 ]]; then 98 | case "$@" in 99 | -h|--help) 100 | echo "usage: pstepw [options] [file/url]" 101 | echo "options:" 102 | echo " -h, --help print help and exit" 103 | echo " -p, --paste upload your clipboard as text" 104 | echo " -s, --selection upload selection scrot" 105 | echo " -v, --version print version and exit" 106 | exit 0 107 | ;; 108 | -s|--selection) 109 | selection 110 | ;; 111 | -v|--version) 112 | echo "pstepw 0.9.1" 113 | exit 0 114 | ;; 115 | -p|--paste) 116 | clipboard 117 | ;; 118 | http*) 119 | location="$@" 120 | url 121 | ;; 122 | *) 123 | location="$@" 124 | file 125 | ;; 126 | esac 127 | else 128 | desktop 129 | fi 130 | 131 | 132 | if [[ "$#" -eq 0 ]]; then 133 | desktop 134 | fi 135 | 136 | upload 137 | feedback 138 | -------------------------------------------------------------------------------- /resources/views/account/uploads.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.account') 2 | 3 | @section('title', 'My Uploads') 4 | 5 | @section('stylesheets') 6 | 7 | @stop 8 | 9 | @section('content') 10 | @if(count($uploads)) 11 |
12 |
13 | 14 | {!! csrf_field() !!} 15 |
16 |
17 |
18 | @foreach ($uploads->chunk(2) as $chunk) 19 |
20 | @foreach ($chunk as $upload) 21 |
22 |
23 |
24 |
25 | 30 |
31 |
32 |

{{ str_limit($upload->original_name, 30) }}

33 |

{{ str_limit($upload->original_name, 25) }}

34 |

{{ str_limit($upload->original_name, 20) }}

35 |

{{ str_limit($upload->original_name, 15) }}

36 |
URL: {{ route('files.get', $upload) }}
37 |
Size: {{ App\Helpers::formatBytes($upload->size) }}
38 |
Views: {{ $upload->views }}
39 |
Uploaded: {{ $upload->created_at->copy()->tz(Auth::user()->preferences->timezone) }}
40 |
41 | 42 |
43 |
44 | 45 | {!! csrf_field() !!} 46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | @endforeach 54 |
55 | @endforeach 56 |
57 | {!! $uploads->render() !!} 58 |
59 | @else 60 |
61 |
{{ trans('messages.no_uploads_found') }}
62 |
63 | @endif 64 | @stop 65 | 66 | @section('javascript') 67 | 68 | @endsection 69 | -------------------------------------------------------------------------------- /resources/views/admin/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.admin') 2 | 3 | @section('title', 'AdminCP') 4 | 5 | @section('content') 6 |
7 |

Install Gentoo

8 |
9 | @stop -------------------------------------------------------------------------------- /resources/views/admin/requests.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.admin') 2 | 3 | @section('title', 'AdminCP - Requests') 4 | 5 | @section('content') 6 | @if ($requestCount) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | @foreach($users as $user) 18 | 19 | 20 | 21 | 22 | 40 | 41 | @endforeach 42 | 43 |
NameEmailDateActions
{{ $user->name }}{{ $user->email }}{{ $user->created_at }} 23 |
    24 |
  • 25 |
    27 | 28 | {!! csrf_field() !!} 29 |
    30 |
  • 31 |
  • 32 |
    34 | 35 | {!! csrf_field() !!} 36 |
    37 |
  • 38 |
39 |
44 |
45 | {!! $users->render() !!} 46 |
47 | @else 48 |
49 |
There are no pending account requests.
50 |
51 | @endif 52 | @stop -------------------------------------------------------------------------------- /resources/views/admin/uploads.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.admin') 2 | 3 | @section('title', 'AdminCP - Uploads') 4 | 5 | @section('stylesheets') 6 | 7 | @stop 8 | 9 | @section('content') 10 | @if($uploads->count()) 11 |
12 |

{{ App\Helpers::properize($user->name) }} Uploads

13 |

Total: {{ $user->getUploadsCount() }} ({{ App\Helpers::formatBytes($user->getUploadsSize()) }})

14 |
15 |
16 | @foreach ($uploads->chunk(2) as $chunk) 17 |
18 | @foreach ($chunk as $upload) 19 |
20 |
21 |
22 |
23 | 28 |
29 |
30 |

{{ str_limit($upload->original_name, 30) }}

31 |

{{ str_limit($upload->original_name, 25) }}

32 |

{{ str_limit($upload->original_name, 20) }}

33 |

{{ str_limit($upload->original_name, 15) }}

34 |
URL: {{ route('files.get', $upload) }}
35 |
Size: {{ App\Helpers::formatBytes($upload->size) }}
36 |
Views: {{ $upload->views }}
37 |
Uploaded: {{ $upload->created_at->copy()->tz(Auth::user()->preferences->timezone) }}
38 |
39 | 40 |
41 |
42 | 43 | {!! csrf_field() !!} 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | @endforeach 52 |
53 | @endforeach 54 |
55 | {!! $uploads->render() !!} 56 |
57 | @else 58 |
59 |
{{ trans('messages.admin.no_uploads_found', ['name' => $user->name]) }}
60 |
61 | @endif 62 | @stop 63 | 64 | @section('javascript') 65 | 66 | @endsection 67 | -------------------------------------------------------------------------------- /resources/views/admin/users.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.admin') 2 | 3 | @section('title', 'AdminCP - Users') 4 | 5 | @section('content') 6 | @if (count($users)) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | @foreach($users as $user) 19 | @if ($user->banned) 20 | 21 | @elseif ($user->admin) 22 | 23 | @else 24 | 25 | @endif 26 | 30 | 32 | 33 | 34 | 65 | 66 | @endforeach 67 | 68 |
NameEmailRegisteredUploadsActions
27 | {{ $user->name }} 29 | {{ $user->email }}{{ $user->created_at->copy()->tz(Auth::user()->preferences->timezone) }}{{ $user->getUploadsCount() }} ({{ App\Helpers::formatBytes($user->getUploadsSize()) }}) 35 |
    36 | @if (!$user->banned) 37 |
  • 38 |
    41 | 42 | {!! csrf_field() !!} 43 |
    44 |
  • 45 | @else 46 |
  • 47 |
    50 | 51 | {!! csrf_field() !!} 52 |
    53 |
  • 54 | @endif 55 |
  • 56 |
    59 | 60 | {!! csrf_field() !!} 61 |
    62 |
  • 63 |
64 |
69 |
70 | {!! $users->render() !!} 71 |
72 | @else 73 |
74 |
There are no users...somehow?
75 |
76 | @endif 77 | @stop -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('title', 'Login') 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 | 19 |
20 | Forgot Password 21 | 22 |
23 | {!! csrf_field() !!} 24 |
25 |
26 | @stop -------------------------------------------------------------------------------- /resources/views/auth/password.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('title', 'Reset Password') 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 10 |
11 | @if(config('upste.recaptcha_enabled')) 12 |
13 | {!! Recaptcha::render() !!} 14 |
15 | @endif 16 |
17 | 18 |
19 | {!! csrf_field() !!} 20 |
21 |
22 | @stop -------------------------------------------------------------------------------- /resources/views/auth/register.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('title', config('upste.require_user_approval') ? 'Request Account' : 'Register Account') 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 10 |
11 |
12 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | @if(config('upste.recaptcha_enabled')) 22 |
23 | {!! Recaptcha::render() !!} 24 |
25 | @endif 26 | {!! Honeypot::generate('my_name', 'my_time') !!} 27 |
28 | 29 |
30 | {!! csrf_field() !!} 31 |
32 |
33 | @stop -------------------------------------------------------------------------------- /resources/views/auth/reset.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('title', 'Reset Password') 4 | 5 | @section('content') 6 |
7 |
8 |
9 | 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 | {!! csrf_field() !!} 23 |
24 |
25 | @stop -------------------------------------------------------------------------------- /resources/views/emails/admin/exception.blade.php: -------------------------------------------------------------------------------- 1 | Client: {{ $ip }} 2 | URL: {{ $url }} 3 | 4 | {!! $exception !!} -------------------------------------------------------------------------------- /resources/views/emails/admin/new_registration.blade.php: -------------------------------------------------------------------------------- 1 | {{ $user->name }} ({{ $user->email }}) has just registered at {{ config('upste.site_name') }}. 2 | @if (config('upste.require_user_approval')) 3 | 4 | Check pending requests at {{ $requestRoute }} 5 | @endif -------------------------------------------------------------------------------- /resources/views/emails/user/account_accepted.blade.php: -------------------------------------------------------------------------------- 1 | Hi {{ $user->name }}, 2 | 3 | Your request for an account at {{ config('upste.site_name') }} was just accepted! 4 | Log in at {{ $loginRoute }} and don't forget to read the new user notice. 5 | -------------------------------------------------------------------------------- /resources/views/emails/user/account_confirmation.blade.php: -------------------------------------------------------------------------------- 1 | Hi {{ $user->name }}, 2 | 3 | Your registration at {{ config('upste.site_name') }} will need to be confirmed before you can proceed any further. Please click below to confirm your email. 4 | {{ $confirmRoute }} 5 | -------------------------------------------------------------------------------- /resources/views/emails/user/account_rejected.blade.php: -------------------------------------------------------------------------------- 1 | Hi {{ $user->name }}, 2 | 3 | Unfortunately, your request for an account at {{ config('upste.site_name') }} was rejected. 4 | This is most likely because the site owner doesn't know you or wasn't expecting your request. 5 | 6 | @if(config('upste.irc_server') && config('upste.irc_channel')) 7 | If you feel this is a mistake, please feel free to come by {{ config('upste.irc_channel') }} on {{ config('upste.irc_server') }} and let us know. 8 | @endif -------------------------------------------------------------------------------- /resources/views/emails/user/api_key_reset.blade.php: -------------------------------------------------------------------------------- 1 | Hi {{ $user->name }}, 2 | 3 | Somebody, probably you, just reset your API key at {{ config('upste.site_name') }}. 4 | If this was you, you can safely ignore this email. 5 | If not, we recommend resetting your password at {{ $passwordRoute }} (you'll need to be logged out first). -------------------------------------------------------------------------------- /resources/views/emails/user/password_reset.blade.php: -------------------------------------------------------------------------------- 1 | Go here to reset your password: {{ $passwordRoute . "/$token" }} -------------------------------------------------------------------------------- /resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 404 - Page Not Found 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

{{ $exception->getMessage() ?: 'Page Not Found' }}

16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | We'll be right back. 8 | 9 | 10 | 11 | 12 |
13 |
14 |

{{ $exception->getMessage() ?: 'Down for maintenance, we\'ll be right back.' }}

15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/views/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('title', config('upste.site_name')) 4 | 5 | @section('content') 6 |
7 |

Welcome to {{ config('upste.site_name') }}

8 |

{{ config('upste.site_name') }} is a {{ config('upste.require_user_approval') ? 'private ' : '' }}file hosting website powered by uPste

9 | @if (config('upste.require_user_approval')) 10 | @if (config('upste.owner_name') && config('upste.owner_email')) 11 |

Accounts are given with approval from {{ config('upste.owner_name') }} <{{ config('upste.owner_email') }}>.

13 | @endif 14 | @endif 15 | Login 16 | {{ config('upste.require_user_approval') ? 'Request' : 'Register' }} Account 17 |
18 | @stop 19 | -------------------------------------------------------------------------------- /resources/views/layouts/account.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('nav-left') 4 |
  •   My Uploads
  • 5 |
  •   Resources
  • 6 |
  •   FAQ
  • 7 | @stop 8 | 9 | @section('nav-right') 10 | 11 | @if (Auth::user()->admin) 12 |
  •   Admin
  • 13 | @endif 14 | @stop -------------------------------------------------------------------------------- /resources/views/layouts/admin.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.master') 2 | 3 | @section('nav-left') 4 | @if (config('upste.require_user_approval')) 5 |
  • 6 | 7 | 8 | {{ $requestCount }} 9 |   Pending Requests 10 | 11 |
  • 12 | @endif 13 |
  •   Users
  • 14 | @stop 15 | -------------------------------------------------------------------------------- /resources/views/layouts/master.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | @yield('title') 9 | 10 | 11 | @yield('stylesheets') 12 | 13 | 14 | 50 |
    51 |
    52 | @include('vendor.flash.message') 53 |
    54 | @yield('content') 55 |
    56 | 66 | 67 | @yield('javascript') 68 | 69 | -------------------------------------------------------------------------------- /resources/views/vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheReverend403/uPste/78de9db3c3b47423ee6ac1212560ed5f14011e6f/resources/views/vendor/.gitkeep -------------------------------------------------------------------------------- /resources/views/vendor/flash/message.blade.php: -------------------------------------------------------------------------------- 1 | @if (Session::has('flash_notification.message')) 2 | @if (Session::has('flash_notification.overlay')) 3 | @include('flash::modal', ['modalClass' => 'flash-modal', 'title' => Session::get('flash_notification.title'), 'body' => Session::get('flash_notification.message')]) 4 | @else 5 |
    6 | {{ Session::get('flash_notification.message') }} 7 |
    8 | @endif 9 | @endif 10 | @if (Session::has('status')) 11 |
    12 | {{ Session::get('status') }} 13 |
    14 | @endif 15 | @if (count($errors) > 0) 16 | @foreach ($errors->all() as $error) 17 | @if (!str_contains($error, 'g-recaptcha-response')) 18 |
    {{ $error }}
    19 | @endif 20 | @endforeach 21 | @endif -------------------------------------------------------------------------------- /server.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 | -------------------------------------------------------------------------------- /storage/.gitignore: -------------------------------------------------------------------------------- 1 | database.sqlite 2 | !.gitignore -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | -------------------------------------------------------------------------------- /storage/app/thumbnails/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/uploads/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | routes.php 3 | compiled.php 4 | services.json 5 | events.scanned.php 6 | routes.scanned.php 7 | down 8 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/ExampleTest.php: -------------------------------------------------------------------------------- 1 | 'TestUser', 25 | 'email' => 'test@test.com', 26 | 'apikey' => str_random(Helpers::API_KEY_LENGTH), 27 | 'password' => Hash::make(str_random(16), ['rounds' => config('upste.password_hash_rounds')]), 28 | ]); 29 | 30 | $this->assertTrue(User::all()->count() == 1); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); 22 | 23 | return $app; 24 | } 25 | } 26 | --------------------------------------------------------------------------------