Commit eaa09168 by 刘旋尧

init

parents
{
"plugins": ["syntax-dynamic-import"]
}
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.{vue,js,json,html,scss,blade.php}]
indent_style = space
indent_size = 2
APP_NAME=LaravelVue
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
JWT_TTL=1440
{
"root": true,
"parserOptions": {
"parser": "babel-eslint",
"ecmaVersion": 2017,
"sourceType": "module"
},
"extends": [
"plugin:vue/recommended",
"@vue/standard"
],
"rules": {
"vue/max-attributes-per-line": "off"
}
}
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
README.md export-ignore
.travis.yml export-ignore
.env.dusk.local export-ignore
.env.dusk.testing export-ignore
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
/.idea
/.vagrant
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.env
/public/js
/public/css
/public/mix-manifest.json
phpunit.dusk.xml
MIT License
Copyright (c) 2017 Cretu Eusebiu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class Event
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\Hook;
class HookEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $hook;
public $data;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Hook $hook,$data)
{
$this->hook=$hook;
$this->data=$data;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
<?php
namespace App\Exceptions;
use Exception;
class EmailTakenException extends Exception
{
/**
* Render the exception as an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
return response()->view('oauth.emailTaken', [], 400);
}
}
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Hook extends Model
{
protected $table='hook';
public static function getHookByName($name){
return self::where("name",$name)->firstOrFail();
}
public static function list(){
return (new self())->orderBy("id","desc")->get();
}
public static function add($data){
$hook=new self();
$hook->name=$data['name'];
$hook->desc=$data['desc'];
$hook->uid=request()->user()->id;
$hook->execute_command=$data['execute_command'];
$hook->command_working_directory=$data['command_working_directory'];
if ($hook->save()) {
return true;
}
return false;
}
public function edit($data){
$this->name=$data['name'];
$this->desc=$data['desc'];
$this->execute_command=$data['execute_command'];
$this->command_working_directory=$data['command_working_directory'];
if ($this->save()) {
return true;
}
return false;
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class HookLog extends Model
{
protected $table='hook_log';
public static function addLog($hook, $data)
{
$log=new self;
$log->hook_id=$hook->id;
$log->result=$data['return_var'];
$log->output=json_encode($data['output']);
return $log->save();
}
public static function getLogByHookId($hookId){
return self::where('hook_id',$hookId)->orderBy('id','desc')->paginate(15);
}
}
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Hook;
use App\Http\Resources\HookCollection;
use App\Rules\HookNameCheckor;
class HookController extends Controller
{
public function list()
{
return new HookCollection(Hook::list());
}
public function add(Request $request)
{
$this->validate($request, [
'name'=>new HookNameCheckor(),
'desc'=>'required',
'execute_command'=>'required'
]);
if (Hook::add($request->all())) {
return response()->json(['msg'=>'success']);
} else {
return response()->json(['exception'=>'failed'], 500);
}
}
public function edit(Request $request, $id)
{
$this->validate($request, [
'name'=>'required',
'desc'=>'required',
'execute_command'=>'required'
]);
$hook=Hook::findOrFail($id);
if ($hook->edit($request->all())) {
return response()->json(['msg'=>'success']);
} else {
return response()->json(['exception'=>'failed'], 500);
}
}
}
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\HookLog;
class HookLogController extends Controller
{
public function list($id){
return HookLog::getLogByHookId($id);
}
}
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get the response for a successful password reset link.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkResponse($response)
{
return ['status' => trans($response)];
}
/**
* Get the response for a failed password reset link.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return response()->json(['email' => trans($response)], 400);
}
}
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
/**
* Attempt to log the user into the application.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function attemptLogin(Request $request)
{
$token = $this->guard()->attempt($this->credentials($request));
if ($token) {
$this->guard()->setToken($token);
return true;
}
return false;
}
/**
* Send the response after the user was authenticated.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function sendLoginResponse(Request $request)
{
$this->clearLoginAttempts($request);
$token = (string) $this->guard()->getToken();
$expiration = $this->guard()->getPayload()->get('exp');
return [
'token' => $token,
'token_type' => 'bearer',
'expires_in' => $expiration - time(),
];
}
/**
* Log the user out of the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$this->guard()->logout();
}
}
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\OAuthProvider;
use App\Http\Controllers\Controller;
use App\Exceptions\EmailTakenException;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class OAuthController extends Controller
{
use AuthenticatesUsers;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
config([
'services.github.redirect' => route('oauth.callback', 'github'),
]);
}
/**
* Redirect the user to the provider authentication page.
*
* @param string $provider
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectToProvider($provider)
{
return [
'url' => Socialite::driver($provider)->stateless()->redirect()->getTargetUrl(),
];
}
/**
* Obtain the user information from the provider.
*
* @param string $driver
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback($provider)
{
$user = Socialite::driver($provider)->stateless()->user();
$user = $this->findOrCreateUser($provider, $user);
$this->guard()->setToken(
$token = $this->guard()->login($user)
);
return view('oauth/callback', [
'token' => $token,
'token_type' => 'bearer',
'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
]);
}
/**
* @param string $provider
* @param \Laravel\Socialite\Contracts\User $sUser
* @return \App\User|false
*/
protected function findOrCreateUser($provider, $user)
{
$oauthProvider = OAuthProvider::where('provider', $provider)
->where('provider_user_id', $user->getId())
->first();
if ($oauthProvider) {
$oauthProvider->update([
'access_token' => $user->token,
'refresh_token' => $user->refreshToken,
]);
return $oauthProvider->user;
}
if (User::where('email', $user->getEmail())->exists()) {
throw new EmailTakenException;
}
return $this->createUser($provider, $user);
}
/**
* @param string $provider
* @param \Laravel\Socialite\Contracts\User $sUser
* @return \App\User
*/
protected function createUser($provider, $sUser)
{
$user = User::create([
'name' => $sUser->getName(),
'email' => $sUser->getEmail(),
]);
$user->oauthProviders()->create([
'provider' => $provider,
'provider_user_id' => $sUser->getId(),
'access_token' => $sUser->token,
'refresh_token' => $sUser->refreshToken,
]);
return $user;
}
}
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller
{
use RegistersUsers;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* The user has been registered.
*
* @param \Illuminate\Http\Request $request
* @param mixed $user
* @return mixed
*/
protected function registered(Request $request, $user)
{
return $user;
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
use ResetsPasswords;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get the response for a successful password reset.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetResponse($response)
{
return ['status' => trans($response)];
}
/**
* Get the response for a failed password reset.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetFailedResponse(Request $request, $response)
{
return response()->json(['email' => trans($response)], 400);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Jobs\ProcessHook;
use App\Hook;
class HookController extends Controller
{
//
public function index(Request $request){
$hook=Hook::getHookByName($request->id);
$job=new ProcessHook($hook,$request->all());
$this->dispatch($job);
return $request->id;
}
}
<?php
namespace App\Http\Controllers\Settings;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PasswordController extends Controller
{
/**
* Update the user's password.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
$this->validate($request, [
'password' => 'required|confirmed|min:6',
]);
$request->user()->update([
'password' => bcrypt($request->password),
]);
}
}
<?php
namespace App\Http\Controllers\Settings;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class ProfileController extends Controller
{
/**
* Update the user's profile information.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
$user = $request->user();
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email,'.$user->id,
]);
return tap($user)->update($request->only('name', 'email'));
}
}
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\SetLocale::class,
];
/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
// \App\Http\Middleware\EncryptCookies::class,
// \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
// \Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
// \Illuminate\View\Middleware\ShareErrorsFromSession::class,
// \App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
}
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array
*/
protected $except = [
//
];
}
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return response()->json(['error' => 'Already authenticated.'], 400);
}
return $next($request);
}
}
<?php
namespace App\Http\Middleware;
use Closure;
class SetLocale
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($locale = $this->parseLocale($request)) {
app()->setLocale($locale);
}
return $next($request);
}
/**
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function parseLocale($request)
{
$locales = config('app.locales');
$locale = $request->server('HTTP_ACCEPT_LANGUAGE');
$locale = substr($locale, 0, strpos($locale, ',') ?: strlen($locale));
if (array_key_exists($locale, $locales)) {
return $locale;
}
if (array_key_exists($locale, $locales)) {
return $locale;
}
$locale = substr($locale, 0, 2);
if (array_key_exists($locale, $locales)) {
return $locale;
}
}
}
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
*/
protected $except = [
'password',
'password_confirmation',
];
}
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array
*/
protected $proxies;
/**
* The current proxy header mappings.
*
* @var array
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
];
}
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class HookCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$dataById=[];
foreach($this->collection->all() as $item){
$dataById['id_'.$item->id]=$item;
}
return [
'data' => $this->collection,
'dataById' => $dataById
];
}
}
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Events\HookEvent;
class ProcessHook implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $hook=null;
protected $data=[];
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($hook, $data)
{
$this->hook=$hook;
$this->data=$data;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$command=sprintf("cd %s && %s", $this->hook->command_working_directory, $this->hook->execute_command);
$output='';
$return_var='';
$rs= exec($command, $output, $return_var);
var_dump($rs, $output, $return_var);
event(new HookEvent($this->hook, ['output'=>$output,'return_var'=>$return_var]));
}
}
<?php
namespace App\Listeners;
use App\Events\Event;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class EventListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param Event $event
* @return void
*/
public function handle(Event $event)
{
//
}
}
<?php
namespace App\Listeners;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\HookLog;
class HookEventListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
HookLog::addLog($event->hook,$event->data);
}
}
<?php
namespace App\Notifications;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Auth\Notifications\ResetPassword as Notification;
class ResetPassword extends Notification
{
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url(config('app.url').'/password/reset/'.$this->token).'?email='.urlencode($notifiable->email))
->line('If you did not request a password reset, no further action is required.');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class OAuthProvider extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'oauth_providers';
/**
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = ['id'];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'access_token', 'refresh_token',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
<?php
namespace App\Providers;
use Laravel\Dusk\DuskServiceProvider;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
if ($this->app->runningUnitTests()) {
Schema::defaultStringLength(191);
}
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
if ($this->app->environment('local', 'testing')) {
$this->app->register(DuskServiceProvider::class);
}
}
}
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\Event' => [
'App\Listeners\EventListener',
],
'App\Events\HookEvent' => [
'App\Listeners\HookEventListener',
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();
}
}
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
//
parent::boot();
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
}
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use App\Hook;
class HookNameCheckor implements Rule
{
protected $message='';
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
if(!$value){
$this->message="{$attribute} 字段必须填写";
return false;
}
$hook = Hook::where('name', $value)->first();
if(!empty($hook)){
$this->message="钩子标题{$value}已经存在";
return false;
}
return true;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return $this->message;
}
}
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Notifications\ResetPassword as ResetPasswordNotification;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The accessors to append to the model's array form.
*
* @var array
*/
protected $appends = [
'photo_url',
];
/**
* Get the profile photo URL attribute.
*
* @return string
*/
public function getPhotoUrlAttribute()
{
return 'https://www.gravatar.com/avatar/'.md5(strtolower($this->email)).'.jpg?s=200&d=mm';
}
/**
* Get the oauth providers.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function oauthProviders()
{
return $this->hasMany(OAuthProvider::class);
}
/**
* Send the password reset notification.
*
* @param string $token
* @return void
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
/**
* @return int
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;
{
"name": "cretueusebiu/laravel-vue-spa",
"description": "A Laravel-Vue SPA starter project template.",
"keywords": ["spa", "laravel", "vue"],
"license": "MIT",
"type": "project",
"require": {
"php": "^7.1.3",
"fideloper/proxy": "^4.0",
"laravel/framework": "5.6.*",
"laravel/socialite": "^3.0",
"laravel/tinker": "~1.0",
"predis/predis": "^1.1",
"tymon/jwt-auth": "^1.0.0-rc.2"
},
"require-dev": {
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"laravel/dusk": "^3.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
},
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/dusk"
]
}
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
],
"post-create-project-cmd": [
"@php artisan key:generate",
"@php artisan jwt:secret --force"
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application. This value is used when the
| framework needs to place the application's name in a notification or
| any other location as required by the application or its packages.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services your application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| your application so that it is used when running Artisan tasks.
|
*/
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'en',
'locales' => [
'en' => 'EN',
'zh-CN' => '中文',
'es' => 'ES',
],
/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/
'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is used by the Illuminate encrypter service and should be set
| to a random, 32 character string, otherwise these encrypted strings
| will not be safe. Please do this before deploying an application!
|
*/
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Logging Configuration
|--------------------------------------------------------------------------
|
| Here you may configure the log settings for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Settings: "single", "daily", "syslog", "errorlog"
|
*/
'log' => env('APP_LOG', 'single'),
'log_level' => env('APP_LOG_LEVEL', 'debug'),
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
|
| The service providers listed here will be automatically loaded on the
| request to your application. Feel free to add your own services to
| this array to grant expanded functionality to your applications.
|
*/
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
|
| This array of class aliases will be registered when this application
| is started. However, feel free to register as many as you wish as
| the aliases are "lazy" loaded so they don't hinder performance.
|
*/
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class,
'DB' => Illuminate\Support\Facades\DB::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
'Redis' => Illuminate\Support\Facades\Redis::class,
'Request' => Illuminate\Support\Facades\Request::class,
'Response' => Illuminate\Support\Facades\Response::class,
'Route' => Illuminate\Support\Facades\Route::class,
'Schema' => Illuminate\Support\Facades\Schema::class,
'Session' => Illuminate\Support\Facades\Session::class,
'Storage' => Illuminate\Support\Facades\Storage::class,
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_DRIVER', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
//
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache connection that gets used while
| using this caching library. This connection is used when another is
| not explicitly specified when executing a given caching function.
|
| Supported: "apc", "array", "database", "file", "memcached", "redis"
|
*/
'default' => env('CACHE_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
*/
'stores' => [
'apc' => [
'driver' => 'apc',
],
'array' => [
'driver' => 'array',
],
'database' => [
'driver' => 'database',
'table' => 'cache',
'connection' => null,
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing a RAM based store such as APC or Memcached, there might
| be other applications utilizing the same cache. So, we'll specify a
| value to get prefixed to all our keys so we can avoid collisions.
|
*/
'prefix' => 'laravel',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
'default' => env('DB_CONNECTION', 'mysql'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
],
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'migrations' => 'migrations',
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer set of commands than a typical key-value systems
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => [
'client' => 'predis',
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application. Just store away!
|
*/
'default' => env('FILESYSTEM_DRIVER', 'local'),
/*
|--------------------------------------------------------------------------
| Default Cloud Filesystem Disk
|--------------------------------------------------------------------------
|
| Many applications store files both locally and in the cloud. For this
| reason, you may specify a default "cloud" driver here. This driver
| will be bound as the Cloud disk implementation in the container.
|
*/
'cloud' => env('FILESYSTEM_CLOUD', 's3'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Here you may configure as many filesystem "disks" as you wish, and you
| may even configure multiple disks of the same driver. Defaults have
| been setup for each driver as an example of the required options.
|
| Supported Drivers: "local", "ftp", "s3", "rackspace"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Mail Driver
|--------------------------------------------------------------------------
|
| Laravel supports both SMTP and PHP's "mail" function as drivers for the
| sending of e-mail. You may specify which one you're using throughout
| your application here. By default, Laravel is setup for SMTP mail.
|
| Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses",
| "sparkpost", "log", "array"
|
*/
'driver' => env('MAIL_DRIVER', 'smtp'),
/*
|--------------------------------------------------------------------------
| SMTP Host Address
|--------------------------------------------------------------------------
|
| Here you may provide the host address of the SMTP server used by your
| applications. A default option is provided that is compatible with
| the Mailgun mail service which will provide reliable deliveries.
|
*/
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
/*
|--------------------------------------------------------------------------
| SMTP Host Port
|--------------------------------------------------------------------------
|
| This is the SMTP port used by your application to deliver e-mails to
| users of the application. Like the host we have set this value to
| stay compatible with the Mailgun e-mail application by default.
|
*/
'port' => env('MAIL_PORT', 587),
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all e-mails sent by your application to be sent from
| the same address. Here, you may specify a name and address that is
| used globally for all e-mails that are sent by your application.
|
*/
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
/*
|--------------------------------------------------------------------------
| E-Mail Encryption Protocol
|--------------------------------------------------------------------------
|
| Here you may specify the encryption protocol that should be used when
| the application send e-mail messages. A sensible default using the
| transport layer security protocol should provide great security.
|
*/
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
/*
|--------------------------------------------------------------------------
| SMTP Server Username
|--------------------------------------------------------------------------
|
| If your SMTP server requires a username for authentication, you should
| set it here. This will get used to authenticate with your server on
| connection. You may also set the "password" value below this one.
|
*/
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
/*
|--------------------------------------------------------------------------
| Sendmail System Path
|--------------------------------------------------------------------------
|
| When using the "sendmail" driver to send e-mails, we will need to know
| the path to where Sendmail lives on this server. A default path has
| been provided here, which will work well on most of your systems.
|
*/
'sendmail' => '/usr/sbin/sendmail -bs',
/*
|--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Driver
|--------------------------------------------------------------------------
|
| Laravel's queue API supports an assortment of back-ends via a single
| API, giving you convenient access to each back-end using the same
| syntax for each one. Here you may set the default queue driver.
|
| Supported: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'default' => env('QUEUE_DRIVER', 'sync'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection information for each server that
| is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more.
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
],
'sqs' => [
'driver' => 'sqs',
'key' => 'your-public-key',
'secret' => 'your-secret-key',
'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id',
'queue' => 'your-queue-name',
'region' => 'us-east-1',
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90,
],
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control which database and table are used to store the jobs that
| have failed. You may change them to any database / table you wish.
|
*/
'failed' => [
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Stripe, Mailgun, SparkPost and others. This file provides a sane
| default location for this type of information, allowing packages
| to have a conventional place to find your various credentials.
|
*/
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
],
'ses' => [
'key' => env('SES_KEY'),
'secret' => env('SES_SECRET'),
'region' => 'us-east-1',
],
'sparkpost' => [
'secret' => env('SPARKPOST_SECRET'),
],
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "file", "cookie", "database", "apc",
| "memcached", "redis", "array"
|
*/
'driver' => env('SESSION_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/
'lifetime' => 120,
'expire_on_close' => false,
/*
|--------------------------------------------------------------------------
| Session Encryption
|--------------------------------------------------------------------------
|
| This option allows you to easily specify that all of your session data
| should be encrypted before it is stored. All encryption will be run
| automatically by Laravel and you can use the Session like normal.
|
*/
'encrypt' => false,
/*
|--------------------------------------------------------------------------
| Session File Location
|--------------------------------------------------------------------------
|
| When using the native session driver, we need a location where session
| files may be stored. A default has been set for you but a different
| location may be specified. This is only needed for file sessions.
|
*/
'files' => storage_path('framework/sessions'),
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" or "redis" session drivers, you may specify a
| connection that should be used to manage these sessions. This should
| correspond to a connection in your database configuration options.
|
*/
'connection' => null,
/*
|--------------------------------------------------------------------------
| Session Database Table
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the table we
| should use to manage the sessions. Of course, a sensible default is
| provided for you; however, you are free to change this as needed.
|
*/
'table' => 'sessions',
/*
|--------------------------------------------------------------------------
| Session Cache Store
|--------------------------------------------------------------------------
|
| When using the "apc" or "memcached" session drivers, you may specify a
| cache store that should be used for these sessions. This value must
| correspond with one of the application's configured cache stores.
|
*/
'store' => null,
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery
|--------------------------------------------------------------------------
|
| Some session drivers must manually sweep their storage location to get
| rid of old sessions from storage. Here are the chances that it will
| happen on a given request. By default, the odds are 2 out of 100.
|
*/
'lottery' => [2, 100],
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the cookie used to identify a session
| instance by ID. The name specified here will get used every time a
| new session cookie is created by the framework for every driver.
|
*/
'cookie' => env(
'SESSION_COOKIE',
str_slug(env('APP_NAME', 'laravel'), '_').'_session'
),
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application but you are free to change this when necessary.
|
*/
'path' => '/',
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| Here you may change the domain of the cookie used to identify a session
| in your application. This will determine which domains the cookie is
| available to in your application. A sensible default has been set.
|
*/
'domain' => env('SESSION_DOMAIN', null),
/*
|--------------------------------------------------------------------------
| HTTPS Only Cookies
|--------------------------------------------------------------------------
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you if it can not be done securely.
|
*/
'secure' => env('SESSION_SECURE_COOKIE', false),
/*
|--------------------------------------------------------------------------
| HTTP Access Only
|--------------------------------------------------------------------------
|
| Setting this value to true will prevent JavaScript from accessing the
| value of the cookie and the cookie will only be accessible through
| the HTTP protocol. You are free to modify this option if needed.
|
*/
'http_only' => true,
/*
|--------------------------------------------------------------------------
| Same-Site Cookies
|--------------------------------------------------------------------------
|
| This option determines how your cookies behave when cross-site requests
| take place, and can be used to mitigate CSRF attacks. By default, we
| do not enable this as other CSRF protection services are in place.
|
| Supported: "lax", "strict"
|
*/
'same_site' => null,
];
<?php
return [
/*
|--------------------------------------------------------------------------
| View Storage Paths
|--------------------------------------------------------------------------
|
| Most templating systems load templates from disk. Here you may specify
| an array of paths that should be checked for your views. Of course
| the usual Laravel view path has already been registered for you.
|
*/
'paths' => [
resource_path('views'),
],
/*
|--------------------------------------------------------------------------
| Compiled View Path
|--------------------------------------------------------------------------
|
| This option determines where all the compiled Blade templates will be
| stored for your application. Typically, this is within the storage
| directory. However, as usual, you are free to change this value.
|
*/
'compiled' => realpath(storage_path('framework/views')),
];
<?php
use Faker\Generator as Faker;
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$factory->define(App\User::class, function (Faker $faker) {
static $password;
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
});
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password')->nullable();
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePasswordResetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateOauthProvidersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('oauth_providers', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->string('provider');
$table->string('provider_user_id')->index();
$table->string('access_token')->nullable();
$table->string('refresh_token')->nullable();
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('oauth_providers');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateHookTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('hook', function (Blueprint $table) {
$table->increments('id');
$table->string('name',64);
$table->string("desc",64);
$table->string('execute_command',64);
$table->string('command_working_directory',64);
$table->integer('uid');
$table->dateTime("created_at");
$table->dateTime("updated_at");
$table->unique('name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('hook');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateFailedJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('failed_jobs');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateHookLogTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('hook_log', function (Blueprint $table) {
$table->increments('id');
$table->integer('hook_id');
$table->tinyInteger('result');
$table->string('output', 64);
$table->dateTime("created_at");
$table->dateTime("updated_at");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('hook_log');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateHookOpLogTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('hook_op_log', function (Blueprint $table) {
$table->increments('id');
$table->integer('hook_id');
$table->tinyInteger('op_type');
$table->integer('uid');
$table->string('remark',64);
$table->dateTime("created_at");
$table->dateTime("updated_at");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('hook_op_log');
}
}
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('hook')->insert([
'name' => 'test',
'desc' => 'test',
'execute_command' => 'ls',
'uid'=>1,
'command_working_directory' => '/var/www',
'created_at'=>date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('users')->insert([
'name' => 'usual2970',
'email' => '536464346@qq.com',
'password' => bcrypt('123456!a'),
]);
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"lint": "vue-cli-service lint resources/assets/js"
},
"dependencies": {
"@fortawesome/fontawesome": "^1.1.4",
"@fortawesome/fontawesome-free-brands": "^5.0.8",
"@fortawesome/fontawesome-free-regular": "^5.0.8",
"@fortawesome/fontawesome-free-solid": "^5.0.8",
"@fortawesome/vue-fontawesome": "^0.0.22",
"axios": "^0.18.0",
"bootstrap": "^4.0.0",
"jquery": "^3.3.1",
"js-cookie": "^2.2.0",
"popper.js": "^1.14.1",
"sweetalert2": "^7.15.1",
"vform": "^1.0.0",
"vue": "^2.5.16",
"vue-i18n": "^7.6.0",
"vue-meta": "^1.4.4",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-router-sync": "^5.0.0"
},
"devDependencies": {
"@vue/cli-plugin-eslint": "^3.0.0-beta.6",
"@vue/cli-service": "^3.0.0-beta.6",
"@vue/eslint-config-standard": "^3.0.0-beta.6",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"cross-env": "^5.1.4",
"laravel-mix": "^2.1.1",
"vue-template-compiler": "^2.5.16",
"webpack-bundle-analyzer": "^2.11.1"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
</php>
</phpunit>
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>
<?php
/**
* Laravel - A PHP Framework For Web Artisans.
*
* @author Taylor Otwell <taylor@laravel.com>
*/
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/
require __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/
$app = require_once __DIR__.'/../bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
User-agent: *
Disallow:
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Imported Rule 1" stopProcessing="true">
<match url="^(.*)/$" ignoreCase="false" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Redirect" redirectType="Permanent" url="/{R:1}" />
</rule>
<rule name="Imported Rule 2" stopProcessing="true">
<match url="^" ignoreCase="false" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
import Vue from 'vue'
import store from '~/store'
import router from '~/router'
import i18n from '~/plugins/i18n'
import App from '~/components/App'
import '~/plugins'
import '~/components'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
i18n,
store,
router,
...App
})
<template>
<div id="app">
<loading ref="loading"/>
<transition name="page" mode="out-in">
<component v-if="layout" :is="layout"/>
</transition>
</div>
</template>
<script>
import Loading from './Loading'
// Load layout components dynamically.
const requireContext = require.context('~/layouts', false, /.*\.vue$/)
const layouts = requireContext.keys()
.map(file =>
[file.replace(/(^.\/)|(\.vue$)/g, ''), requireContext(file)]
)
.reduce((components, [name, component]) => {
components[name] = component.default || component
return components
}, {})
export default {
el: '#app',
components: {
Loading
},
data: () => ({
layout: null,
defaultLayout: 'default'
}),
metaInfo () {
const { appName } = window.config
return {
title: appName,
titleTemplate: `%s · ${appName}`
}
},
mounted () {
this.$loading = this.$refs.loading
},
methods: {
/**
* Set the application layout.
*
* @param {String} layout
*/
setLayout (layout) {
if (!layout || !layouts[layout]) {
layout = this.defaultLayout
}
this.layout = layouts[layout]
}
}
}
</script>
<template>
<button :type="nativeType" :disabled="loading" :class="{
[`btn-${type}`]: true,
'btn-block': block,
'btn-lg': large,
'btn-loading': loading
}" class="btn">
<slot/>
</button>
</template>
<script>
export default {
name: 'VButton',
props: {
type: {
type: String,
default: 'primary'
},
nativeType: {
type: String,
default: 'submit'
},
loading: {
type: Boolean,
default: false
},
block: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
}
}
}
</script>
<template>
<div class="card">
<div v-if="title" class="card-header">
{{ title }}
</div>
<div class="card-body">
<slot/>
</div>
</div>
</template>
<script>
export default {
name: 'Card',
props: {
title: { type: String, default: null }
}
}
</script>
<template>
<div class="custom-control custom-checkbox d-flex">
<input
:name="name"
:checked="internalValue"
:id="id || name"
type="checkbox"
class="custom-control-input"
@click="handleClick">
<label :for="id || name" class="custom-control-label my-auto">
<slot/>
</label>
</div>
</template>
<script>
export default {
name: 'Checkbox',
props: {
id: { type: String, default: null },
name: { type: String, default: 'checkbox' },
value: { type: Boolean, default: false },
checked: { type: Boolean, default: false }
},
data: () => ({
internalValue: false
}),
watch: {
value (val) {
this.internalValue = val
},
checked (val) {
this.internalValue = val
},
internalValue (val, oldVal) {
if (val !== oldVal) {
this.$emit('input', val)
}
}
},
created () {
this.internalValue = this.value
if ('checked' in this.$options.propsData) {
this.internalValue = this.checked
}
},
methods: {
handleClick (e) {
this.$emit('click', e)
if (!e.isPropagationStopped) {
this.internalValue = e.target.checked
}
}
}
}
</script>
<template>
<transition name="page" mode="out-in">
<slot>
<router-view/>
</slot>
</transition>
</template>
<script>
export default {
name: 'Child'
}
</script>
<template>
<div :style="{
width: `${percent}%`,
height: height,
opacity: show ? 1 : 0,
'background-color': canSuccess ? color : failedColor
}" class="progress"/>
</template>
<script>
// https://github.com/nuxt/nuxt.js/blob/master/lib/app/components/nuxt-loading.vue
export default {
data: () => ({
percent: 0,
show: false,
canSuccess: true,
duration: 3000,
height: '2px',
color: '#77b6ff',
failedColor: 'red'
}),
methods: {
start () {
this.show = true
this.canSuccess = true
if (this._timer) {
clearInterval(this._timer)
this.percent = 0
}
this._cut = 10000 / Math.floor(this.duration)
this._timer = setInterval(() => {
this.increase(this._cut * Math.random())
if (this.percent > 95) {
this.finish()
}
}, 100)
return this
},
set (num) {
this.show = true
this.canSuccess = true
this.percent = Math.floor(num)
return this
},
get () {
return Math.floor(this.percent)
},
increase (num) {
this.percent = this.percent + Math.floor(num)
return this
},
decrease (num) {
this.percent = this.percent - Math.floor(num)
return this
},
finish () {
this.percent = 100
this.hide()
return this
},
pause () {
clearInterval(this._timer)
return this
},
hide () {
clearInterval(this._timer)
this._timer = null
setTimeout(() => {
this.show = false
this.$nextTick(() => {
setTimeout(() => {
this.percent = 0
}, 200)
})
}, 500)
return this
},
fail () {
this.canSuccess = false
return this
}
}
}
</script>
<style scoped>
.progress {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
height: 2px;
width: 0%;
transition: width 0.2s, opacity 0.4s;
opacity: 1;
background-color: #efc14e;
z-index: 999999;
}
</style>
<template>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ locales[locale] }}
</a>
<div class="dropdown-menu">
<a v-for="(value, key) in locales" :key="key" class="dropdown-item" href="#"
@click.prevent="setLocale(key)">
{{ value }}
</a>
</div>
</li>
</template>
<script>
import { mapGetters } from 'vuex'
import { loadMessages } from '~/plugins/i18n'
export default {
computed: mapGetters({
locale: 'lang/locale',
locales: 'lang/locales'
}),
methods: {
setLocale (locale) {
if (this.$i18n.locale !== locale) {
loadMessages(locale)
this.$store.dispatch('lang/setLocale', { locale })
}
}
}
}
</script>
<template>
<button v-if="githubAuth" class="btn btn-dark ml-auto" type="button" @click="login">
{{ $t('login_with') }}
<fa :icon="['fab', 'github']"/>
</button>
</template>
<script>
export default {
name: 'LoginWithGithub',
computed: {
githubAuth: () => window.config.githubAuth,
url: () => `/api/oauth/github`
},
mounted () {
window.addEventListener('message', this.onMessage, false)
},
beforeDestroy () {
window.removeEventListener('message', this.onMessage)
},
methods: {
async login () {
const url = await this.$store.dispatch('auth/fetchOauthUrl', {
provider: 'github'
})
openWindow(url, this.$t('login'))
},
/**
* @param {MessageEvent} e
*/
onMessage (e) {
if (e.origin !== window.origin || !e.data.token) {
return
}
this.$store.dispatch('auth/saveToken', {
token: e.data.token
})
this.$router.push({ name: 'home' })
}
}
}
/**
* @param {Object} options
* @return {Window}
*/
function openWindow (url, title, options = {}) {
if (typeof url === 'object') {
options = url
url = ''
}
options = { url, title, width: 600, height: 720, ...options }
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screen.left
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screen.top
const width = window.innerWidth || document.documentElement.clientWidth || window.screen.width
const height = window.innerHeight || document.documentElement.clientHeight || window.screen.height
options.left = ((width / 2) - (options.width / 2)) + dualScreenLeft
options.top = ((height / 2) - (options.height / 2)) + dualScreenTop
const optionsStr = Object.keys(options).reduce((acc, key) => {
acc.push(`${key}=${options[key]}`)
return acc
}, []).join(',')
const newWindow = window.open(url, title, optionsStr)
if (window.focus) {
newWindow.focus()
}
return newWindow
}
</script>
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-white">
<div class="container">
<router-link :to="{ name: user ? 'home' : 'welcome' }" class="navbar-brand">
{{ appName }}
</router-link>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarToggler" aria-controls="navbarToggler" aria-expanded="false">
<span class="navbar-toggler-icon"/>
</button>
<div id="navbarToggler" class="collapse navbar-collapse">
<ul class="navbar-nav">
<locale-dropdown/>
<!-- <li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li> -->
</ul>
<ul class="navbar-nav ml-auto">
<!-- Authenticated -->
<li v-if="user" class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-dark"
href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img :src="user.photo_url" class="rounded-circle profile-photo mr-1">
{{ user.name }}
</a>
<div class="dropdown-menu">
<router-link :to="{ name: 'settings.profile' }" class="dropdown-item pl-3">
<fa icon="cog" fixed-width/>
{{ $t('settings') }}
</router-link>
<div class="dropdown-divider"/>
<a href="#" class="dropdown-item pl-3" @click.prevent="logout">
<fa icon="sign-out-alt" fixed-width/>
{{ $t('logout') }}
</a>
</div>
</li>
<!-- Guest -->
<template v-else>
<li class="nav-item">
<router-link :to="{ name: 'login' }" class="nav-link" active-class="active">
{{ $t('login') }}
</router-link>
</li>
<li class="nav-item">
<router-link :to="{ name: 'register' }" class="nav-link" active-class="active">
{{ $t('register') }}
</router-link>
</li>
</template>
</ul>
</div>
</div>
</nav>
</template>
<script>
import { mapGetters } from 'vuex'
import LocaleDropdown from './LocaleDropdown'
export default {
components: {
LocaleDropdown
},
data: () => ({
appName: window.config.appName
}),
computed: mapGetters({
user: 'auth/user'
}),
methods: {
async logout () {
// Log out the user.
await this.$store.dispatch('auth/logout')
// Redirect to login.
this.$router.push({ name: 'login' })
}
}
}
</script>
<style scoped>
.profile-photo {
width: 2rem;
height: 2rem;
margin: -.375rem 0;
}
</style>
import Vue from 'vue'
import Card from './Card'
import Child from './Child'
import Button from './Button'
import Checkbox from './Checkbox'
import { HasError, AlertError, AlertSuccess } from 'vform'
// Components that are registered globaly.
[
Card,
Child,
Button,
Checkbox,
HasError,
AlertError,
AlertSuccess
].forEach(Component => {
Vue.component(Component.name, Component)
})
{
"ok": "Ok",
"cancel": "Cancel",
"error_alert_title": "Oops...",
"error_alert_text": "Something went wrong! Please try again.",
"token_expired_alert_title": "Session Expired!",
"token_expired_alert_text": "Please log in again to continue.",
"login": "Log In",
"register": "Register",
"page_not_found": "Page Not Found",
"go_home": "Go Home",
"logout": "Logout",
"email": "Email",
"remember_me": "Remember Me",
"password": "Password",
"forgot_password": "Forgot Your Password?",
"confirm_password": "Confirm Password",
"name": "Name",
"toggle_navigation": "Toggle navigation",
"home": "Home",
"you_are_logged_in": "You are logged in!",
"reset_password": "Reset Password",
"send_password_reset_link": "Send Password Reset Link",
"settings": "Settings",
"profile": "Profile",
"your_info": "Your Info",
"info_updated": "Your info has been updated!",
"update": "Update",
"your_password": "Your Password",
"password_updated": "Your password has been updated!",
"new_password": "New Password",
"login_with": "Login with",
"register_with": "Register with"
}
{
"ok": "De Acuerdo",
"cancel": "Cancelar",
"error_alert_title": "Ha ocurrido un problema",
"error_alert_text": "¡Algo salió mal! Inténtalo de nuevo.",
"token_expired_alert_title": "!Sesión Expirada!",
"token_expired_alert_text": "Por favor inicie sesión de nuevo para continuar.",
"login": "Iniciar Sesión",
"register": "Registro",
"page_not_found": "Página No Encontrada",
"go_home": "Ir a Inicio",
"logout": "Cerrar Sesión",
"email": "Correo Electrónico",
"remember_me": "Recuérdame",
"password": "Contraseña",
"forgot_password": "¿Olvidaste tu contraseña?",
"confirm_password": "Confirmar Contraseña",
"name": "Nombre",
"toggle_navigation": "Cambiar Navegación",
"home": "Inicio",
"you_are_logged_in": "¡Has iniciado sesión!",
"reset_password": "Restablecer la contraseña",
"send_password_reset_link": "Enviar Enlace de Restablecimiento de Contraseña",
"settings": "Configuraciones",
"profile": "Perfil",
"your_info": "Tu Información",
"info_updated": "¡Tu información ha sido actualizada!",
"update": "Actualizar",
"your_password": "Tu Contraseña",
"password_updated": "¡Tu contraseña ha sido actualizada!",
"new_password": "Nueva Contraseña",
"login_with": "Iniciar Sesión con",
"register_with": "Registro con"
}
{
"ok": "确定",
"cancel": "取消",
"error_alert_title": "错误...",
"error_alert_text": "遇到一些错误,请稍后重试~",
"token_expired_alert_title": "验证过期!",
"token_expired_alert_text": "请稍后重新登录系统",
"login": "登录",
"register": "注册",
"page_not_found": "页面不存在",
"go_home": "返回首页",
"logout": "退出",
"email": "邮箱",
"remember_me": "记住我",
"password": "密码",
"forgot_password": "忘记密码?",
"confirm_password": "重复密码",
"name": "用户名",
"toggle_navigation": "切换导航",
"home": "首页",
"you_are_logged_in": "您已经登录!",
"reset_password": "重置密码",
"send_password_reset_link": "发送重置链接",
"settings": "设置",
"profile": "个人设置",
"your_info": "您的个人信息",
"info_updated": "您的个人信息已经更改!",
"update": "更新",
"your_password": "您的密码",
"password_updated": "您的密码已经更新!",
"new_password": "新密码",
"login_with": "登录",
"register_with": "注册"
}
<template>
<div class="basic-layout d-flex align-items-center justify-content-center m-0 bg-white">
<child/>
</div>
</template>
<script>
export default {
name: 'BasicLayout'
}
</script>
<style lang="scss">
.basic-layout {
color: #636b6f;
height: 100vh;
font-weight: 100;
position: relative;
.links > a {
color: #636b6f;
padding: 0 25px;
font-size: 12px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
}
</style>
<template>
<div class="main-layout">
<navbar/>
<div class="container mt-4">
<child/>
</div>
</div>
</template>
<script>
import Navbar from '~/components/Navbar'
export default {
name: 'MainLayout',
components: {
Navbar
}
}
</script>
import store from '~/store'
export default (to, from, next) => {
if (store.getters['auth/user'].role !== 'admin') {
next({ name: 'home' })
} else {
next()
}
}
import store from '~/store'
export default async (to, from, next) => {
if (!store.getters['auth/check']) {
next({ name: 'login' })
} else {
next()
}
}
import store from '~/store'
export default async (to, from, next) => {
if (!store.getters['auth/check'] && store.getters['auth/token']) {
try {
await store.dispatch('auth/fetchUser')
} catch (e) { }
}
next()
}
import store from '~/store'
export default (to, from, next) => {
if (store.getters['auth/check']) {
next({ name: 'home' })
} else {
next()
}
}
import store from '~/store'
import { loadMessages } from '~/plugins/i18n'
export default async (to, from, next) => {
await loadMessages(store.getters['lang/locale'])
next()
}
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card :title="$t('login')">
<form @submit.prevent="login" @keydown="form.onKeydown($event)">
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('email') }}</label>
<div class="col-md-7">
<input v-model="form.email" :class="{ 'is-invalid': form.errors.has('email') }" class="form-control" type="email" name="email">
<has-error :form="form" field="email"/>
</div>
</div>
<!-- Password -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('password') }}</label>
<div class="col-md-7">
<input v-model="form.password" :class="{ 'is-invalid': form.errors.has('password') }" class="form-control" type="password" name="password">
<has-error :form="form" field="password"/>
</div>
</div>
<!-- Remember Me -->
<div class="form-group row">
<div class="col-md-3"/>
<div class="col-md-7 d-flex">
<checkbox v-model="remember" name="remember">
{{ $t('remember_me') }}
</checkbox>
<router-link :to="{ name: 'password.request' }" class="small ml-auto my-auto">
{{ $t('forgot_password') }}
</router-link>
</div>
</div>
<div class="form-group row">
<div class="col-md-7 offset-md-3 d-flex">
<!-- Submit Button -->
<v-button :loading="form.busy">
{{ $t('login') }}
</v-button>
<!-- GitHub Login Button -->
<login-with-github/>
</div>
</div>
</form>
</card>
</div>
</div>
</template>
<script>
import Form from 'vform'
import LoginWithGithub from '~/components/LoginWithGithub'
export default {
middleware: 'guest',
components: {
LoginWithGithub
},
metaInfo () {
return { title: this.$t('login') }
},
data: () => ({
form: new Form({
email: '',
password: ''
}),
remember: false
}),
methods: {
async login () {
// Submit the form.
const { data } = await this.form.post('/api/login')
// Save the token.
this.$store.dispatch('auth/saveToken', {
token: data.token,
remember: this.remember
})
// Fetch the user.
await this.$store.dispatch('auth/fetchUser')
// Redirect home.
this.$router.push({ name: 'home' })
}
}
}
</script>
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card :title="$t('reset_password')">
<form @submit.prevent="send" @keydown="form.onKeydown($event)">
<alert-success :form="form" :message="status"/>
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('email') }}</label>
<div class="col-md-7">
<input v-model="form.email" :class="{ 'is-invalid': form.errors.has('email') }" class="form-control" type="email" name="email">
<has-error :form="form" field="email"/>
</div>
</div>
<!-- Submit Button -->
<div class="form-group row">
<div class="col-md-9 ml-md-auto">
<v-button :loading="form.busy">
{{ $t('send_password_reset_link') }}
</v-button>
</div>
</div>
</form>
</card>
</div>
</div>
</template>
<script>
import Form from 'vform'
export default {
middleware: 'guest',
metaInfo () {
return { title: this.$t('reset_password') }
},
data: () => ({
status: '',
form: new Form({
email: ''
})
}),
methods: {
async send () {
const { data } = await this.form.post('/api/password/email')
this.status = data.status
this.form.reset()
}
}
}
</script>
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card :title="$t('reset_password')">
<form @submit.prevent="reset" @keydown="form.onKeydown($event)">
<alert-success :form="form" :message="status"/>
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('email') }}</label>
<div class="col-md-7">
<input v-model="form.email" :class="{ 'is-invalid': form.errors.has('email') }" class="form-control" type="email" name="email" readonly>
<has-error :form="form" field="email"/>
</div>
</div>
<!-- Password -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('password') }}</label>
<div class="col-md-7">
<input v-model="form.password" :class="{ 'is-invalid': form.errors.has('password') }" class="form-control" type="password" name="password">
<has-error :form="form" field="password"/>
</div>
</div>
<!-- Password Confirmation -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('confirm_password') }}</label>
<div class="col-md-7">
<input v-model="form.password_confirmation" :class="{ 'is-invalid': form.errors.has('password_confirmation') }" class="form-control" type="password" name="password_confirmation">
<has-error :form="form" field="password_confirmation"/>
</div>
</div>
<!-- Submit Button -->
<div class="form-group row">
<div class="col-md-9 ml-md-auto">
<v-button :loading="form.busy">
{{ $t('reset_password') }}
</v-button>
</div>
</div>
</form>
</card>
</div>
</div>
</template>
<script>
import Form from 'vform'
export default {
middleware: 'guest',
metaInfo () {
return { title: this.$t('reset_password') }
},
data: () => ({
status: '',
form: new Form({
token: '',
email: '',
password: '',
password_confirmation: ''
})
}),
created () {
this.form.email = this.$route.query.email
this.form.token = this.$route.params.token
},
methods: {
async reset () {
const { data } = await this.form.post('/api/password/reset')
this.status = data.status
this.form.reset()
}
}
}
</script>
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card :title="$t('register')">
<form @submit.prevent="register" @keydown="form.onKeydown($event)">
<!-- Name -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('name') }}</label>
<div class="col-md-7">
<input v-model="form.name" :class="{ 'is-invalid': form.errors.has('name') }" class="form-control" type="text" name="name">
<has-error :form="form" field="name"/>
</div>
</div>
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('email') }}</label>
<div class="col-md-7">
<input v-model="form.email" :class="{ 'is-invalid': form.errors.has('email') }" class="form-control" type="email" name="email">
<has-error :form="form" field="email"/>
</div>
</div>
<!-- Password -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('password') }}</label>
<div class="col-md-7">
<input v-model="form.password" :class="{ 'is-invalid': form.errors.has('password') }" class="form-control" type="password" name="password">
<has-error :form="form" field="password"/>
</div>
</div>
<!-- Password Confirmation -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('confirm_password') }}</label>
<div class="col-md-7">
<input v-model="form.password_confirmation" :class="{ 'is-invalid': form.errors.has('password_confirmation') }" class="form-control" type="password" name="password_confirmation">
<has-error :form="form" field="password_confirmation"/>
</div>
</div>
<div class="form-group row">
<div class="col-md-7 offset-md-3 d-flex">
<!-- Submit Button -->
<v-button :loading="form.busy">
{{ $t('register') }}
</v-button>
<!-- GitHub Register Button -->
<login-with-github/>
</div>
</div>
</form>
</card>
</div>
</div>
</template>
<script>
import Form from 'vform'
import LoginWithGithub from '~/components/LoginWithGithub'
export default {
middleware: 'guest',
components: {
LoginWithGithub
},
metaInfo () {
return { title: this.$t('register') }
},
data: () => ({
form: new Form({
name: '',
email: '',
password: '',
password_confirmation: ''
})
}),
methods: {
async register () {
// Register the user.
const { data } = await this.form.post('/api/register')
// Log in the user.
const { data: { token } } = await this.form.post('/api/login')
// Save the token.
this.$store.dispatch('auth/saveToken', { token })
// Update the user.
await this.$store.dispatch('auth/updateUser', { user: data })
// Redirect home.
this.$router.push({ name: 'home' })
}
}
}
</script>
<template>
<card class="text-center">
<h3 class="mb-4">{{ $t('page_not_found') }}</h3>
<div class="links">
<router-link :to="{ name: 'welcome' }">
{{ $t('go_home') }}
</router-link>
</div>
</card>
</template>
<script>
export default {
name: 'NotFound'
}
</script>
<template>
<card :title="$t('home')">
<p class='text-right'>
<router-link to="/add" class='btn btn-primary pull-right'>创建Hook</router-link>
</p>
<hr/>
<table class="table table-bordered">
<thead>
<tr>
<th>名称 </th>
<th>功能描述</th>
<th>命令</th>
<th>目录</th>
<th>创建时间</th>
<th>更新时间</th>
<th width="10%"></th>
</tr>
</thead>
<tbody>
<tr v-for="hook in hooks">
<td>{{hook.name}}</td>
<td>{{hook.desc}}</td>
<td>{{hook.execute_command}}</td>
<td>{{hook.command_working_directory}}</td>
<td>{{hook.created_at}}</td>
<td>{{hook.updated_at}}</td>
<td><router-link :to="'log/'+hook.id">日志</router-link>&nbsp;&nbsp;<router-link :to="'edit/'+hook.id">编辑</router-link></td>
</tr>
</tbody>
</table>
</card>
</template>
<script>
import axios from 'axios'
import { mapGetters } from 'vuex'
export default {
middleware: 'auth',
metaInfo () {
return { title: this.$t('home') }
},
computed:{
...mapGetters({
hooks:'hook/hooks'
})
},
async created(){
this.$store.dispatch('hook/fetchHook')
}
}
</script>
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card title="添加Hook">
<form @submit.prevent="add" @keydown="form.onKeydown($event)">
<!-- Name -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">标题</label>
<div class="col-md-7">
<input v-model="form.name" :class="{ 'is-invalid': form.errors.has('name') }" class="form-control" type="text" name="name">
<has-error :form="form" field="name"/>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">描述</label>
<div class="col-md-7">
<textarea v-model='form.desc' :class="{ 'is-invalid': form.errors.has('desc') }" class="form-control" type="text" name="desc">
</textarea>
<has-error :form="form" field="desc"/>
</div>
</div>
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">命令</label>
<div class="col-md-7">
<input v-model="form.execute_command" :class="{ 'is-invalid': form.errors.has('execute_command') }" class="form-control" type="text" name="execute_command">
<has-error :form="form" field="execute_command"/>
</div>
</div>
<!-- Password -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">目录</label>
<div class="col-md-7">
<input v-model="form.command_working_directory" :class="{ 'is-invalid': form.errors.has('command_working_directory') }" class="form-control" type="text" name="command_working_directory">
<has-error :form="form" field="command_working_directory"/>
</div>
</div>
<div class="form-group row">
<div class="col-md-7 offset-md-3 d-flex">
<!-- Submit Button -->
<v-button :loading="form.busy">
保存
</v-button>
</div>
</div>
</form>
</card>
</div>
</div>
</template>
<script>
import Form from 'vform'
import LoginWithGithub from '~/components/LoginWithGithub'
export default {
middleware: 'auth',
components: {
LoginWithGithub
},
metaInfo () {
return { title: '添加Hook' }
},
data: () => ({
form: new Form({
name: '',
desc: '',
execute_command: '',
command_working_directory: ''
})
}),
methods:{
async add(){
const { data } = await this.form.post('/api/hook/add')
this.$router.push({ name: 'home' })
}
}
}
</script>
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card title="编辑Hook">
<form @submit.prevent="add" @keydown="form.onKeydown($event)">
<!-- Name -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">标题</label>
<div class="col-md-7">
<input v-model="form.name" :class="{ 'is-invalid': form.errors.has('name') }" class="form-control" type="text" name="name">
<has-error :form="form" field="name"/>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">描述</label>
<div class="col-md-7">
<textarea v-model='form.desc' :class="{ 'is-invalid': form.errors.has('desc') }" class="form-control" type="text" name="desc">
</textarea>
<has-error :form="form" field="desc"/>
</div>
</div>
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">命令</label>
<div class="col-md-7">
<input v-model="form.execute_command" :class="{ 'is-invalid': form.errors.has('execute_command') }" class="form-control" type="text" name="execute_command">
<has-error :form="form" field="execute_command"/>
</div>
</div>
<!-- Password -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">目录</label>
<div class="col-md-7">
<input v-model="form.command_working_directory" :class="{ 'is-invalid': form.errors.has('command_working_directory') }" class="form-control" type="text" name="command_working_directory">
<has-error :form="form" field="command_working_directory"/>
</div>
</div>
<div class="form-group row">
<div class="col-md-7 offset-md-3 d-flex">
<!-- Submit Button -->
<v-button :loading="form.busy">
保存
</v-button>
</div>
</div>
</form>
</card>
</div>
</div>
</template>
<script>
import Form from 'vform'
import LoginWithGithub from '~/components/LoginWithGithub'
export default {
middleware: 'auth',
metaInfo () {
return { title: '编辑Hook' }
},
data: () => ({
id:0
}),
computed:{
form(){
const temp=this.$store.getters['hook/hooksById']['id_'+this.id]
return new Form(temp)
}
},
methods:{
async add(){
const { data } = await this.form.post(`/api/hook/edit/${this.id}`)
this.$router.push({ name: 'home' })
}
},
created(){
this.id=this.$route.params.id;
const hooks=this.$store.getters['hook/hooks']
if(hooks.length<=0){
this.$store.dispatch('hook/fetchHook')
}
}
}
</script>
<template>
<div class="row">
<div class="col-lg-8 m-auto">
<card title="Hook日志">
<p>
<router-link to="/home">回到Hook列表</router-link>
</p>
<ul class="list-group list-group-flush">
<li class="list-group-item" v-for="log in logs">
{{log.created_at}} 执行: {{log.result==0?'成功':'失败'}} 输出内容:{{log.output}}
</li>
</ul>
</card>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data(){
return {
page:0,
logs:[],
hook_id:0
}
},
metaInfo () {
return { title: 'Hook日志' }
},
async created () {
this.hook_id=this.$route.params.id;
await this.getLog()
},
methods:{
async getLog(){
const id=this.$route.params.id;
try{
const data= await axios.post(`/api/log/${id}`,{page:this.page+1});
this.page=this.page+1;
this.logs={...this.logs,...data.data.data}
}catch(e){
}
}
}
}
</script>
<template>
<div class="row">
<div class="col-md-3">
<card :title="$t('settings')" class="settings-card">
<ul class="nav flex-column nav-pills">
<li v-for="tab in tabs" :key="tab.route" class="nav-item">
<router-link :to="{ name: tab.route }" class="nav-link" active-class="active">
<fa :icon="tab.icon" fixed-width/>
{{ tab.name }}
</router-link>
</li>
</ul>
</card>
</div>
<div class="col-md-9">
<transition name="fade" mode="out-in">
<router-view/>
</transition>
</div>
</div>
</template>
<script>
export default {
middleware: 'auth',
computed: {
tabs () {
return [
{
icon: 'user',
name: this.$t('profile'),
route: 'settings.profile'
},
{
icon: 'lock',
name: this.$t('password'),
route: 'settings.password'
}
]
}
}
}
</script>
<style>
.settings-card .card-body {
padding: 0;
}
</style>
<template>
<card :title="$t('your_password')">
<form @submit.prevent="update" @keydown="form.onKeydown($event)">
<alert-success :form="form" :message="$t('password_updated')"/>
<!-- Password -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('new_password') }}</label>
<div class="col-md-7">
<input v-model="form.password" :class="{ 'is-invalid': form.errors.has('password') }" class="form-control" type="password" name="password">
<has-error :form="form" field="password"/>
</div>
</div>
<!-- Password Confirmation -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('confirm_password') }}</label>
<div class="col-md-7">
<input v-model="form.password_confirmation" :class="{ 'is-invalid': form.errors.has('password_confirmation') }" class="form-control" type="password" name="password_confirmation">
<has-error :form="form" field="password_confirmation"/>
</div>
</div>
<!-- Submit Button -->
<div class="form-group row">
<div class="col-md-9 ml-md-auto">
<v-button :loading="form.busy" type="success">{{ $t('update') }}</v-button>
</div>
</div>
</form>
</card>
</template>
<script>
import Form from 'vform'
export default {
scrollToTop: false,
metaInfo () {
return { title: this.$t('settings') }
},
data: () => ({
form: new Form({
password: '',
password_confirmation: ''
})
}),
methods: {
async update () {
await this.form.patch('/api/settings/password')
this.form.reset()
}
}
}
</script>
<template>
<card :title="$t('your_info')">
<form @submit.prevent="update" @keydown="form.onKeydown($event)">
<alert-success :form="form" :message="$t('info_updated')"/>
<!-- Name -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('name') }}</label>
<div class="col-md-7">
<input v-model="form.name" :class="{ 'is-invalid': form.errors.has('name') }" class="form-control" type="text" name="name">
<has-error :form="form" field="name"/>
</div>
</div>
<!-- Email -->
<div class="form-group row">
<label class="col-md-3 col-form-label text-md-right">{{ $t('email') }}</label>
<div class="col-md-7">
<input v-model="form.email" :class="{ 'is-invalid': form.errors.has('email') }" class="form-control" type="email" name="email">
<has-error :form="form" field="email" />
</div>
</div>
<!-- Submit Button -->
<div class="form-group row">
<div class="col-md-9 ml-md-auto">
<v-button :loading="form.busy" type="success">{{ $t('update') }}</v-button>
</div>
</div>
</form>
</card>
</template>
<script>
import Form from 'vform'
import { mapGetters } from 'vuex'
export default {
scrollToTop: false,
metaInfo () {
return { title: this.$t('settings') }
},
data: () => ({
form: new Form({
name: '',
email: ''
})
}),
computed: mapGetters({
user: 'auth/user'
}),
created () {
// Fill the form with user data.
this.form.keys().forEach(key => {
this.form[key] = this.user[key]
})
},
methods: {
async update () {
const { data } = await this.form.patch('/api/settings/profile')
this.$store.dispatch('auth/updateUser', { user: data })
}
}
}
</script>
<template>
<div>
<div class="top-right links">
<template v-if="authenticated">
<router-link :to="{ name: 'home' }">
{{ $t('home') }}
</router-link>
</template>
<template v-else>
<router-link :to="{ name: 'login' }">
{{ $t('login') }}
</router-link>
<router-link :to="{ name: 'register' }">
{{ $t('register') }}
</router-link>
</template>
</div>
<div class="text-center">
<div class="title mb-4">
{{ title }}
</div>
<div class="links">
<a href="https://laravel.com/docs">Documentation</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
layout: 'basic',
metaInfo () {
return { title: this.$t('home') }
},
data: () => ({
title: window.config.appName
}),
computed: mapGetters({
authenticated: 'auth/check'
})
}
</script>
<style scoped>
.top-right {
position: absolute;
right: 10px;
top: 18px;
}
.title {
font-size: 85px;
}
</style>
import axios from 'axios'
import store from '~/store'
import router from '~/router'
import swal from 'sweetalert2'
import i18n from '~/plugins/i18n'
// Request interceptor
axios.interceptors.request.use(request => {
const token = store.getters['auth/token']
if (token) {
request.headers.common['Authorization'] = `Bearer ${token}`
}
const locale = store.getters['lang/locale']
if (locale) {
request.headers.common['Accept-Language'] = locale
}
// request.headers['X-Socket-Id'] = Echo.socketId()
return request
})
// Response interceptor
axios.interceptors.response.use(response => response, error => {
const { status } = error.response
if (status >= 500) {
swal({
type: 'error',
title: i18n.t('error_alert_title'),
text: i18n.t('error_alert_text'),
reverseButtons: true,
confirmButtonText: i18n.t('ok'),
cancelButtonText: i18n.t('cancel')
})
}
if (status === 401 && store.getters['auth/check']) {
swal({
type: 'warning',
title: i18n.t('token_expired_alert_title'),
text: i18n.t('token_expired_alert_text'),
reverseButtons: true,
confirmButtonText: i18n.t('ok'),
cancelButtonText: i18n.t('cancel')
}).then(async () => {
await store.dispatch('auth/logout')
router.push({ name: 'login' })
})
}
return Promise.reject(error)
})
import Vue from 'vue'
import fontawesome from '@fortawesome/fontawesome'
import FontAwesomeIcon from '@fortawesome/vue-fontawesome'
// import { } from '@fortawesome/fontawesome-free-regular/shakable.es'
import {
faUser, faLock, faSignOutAlt, faCog
} from '@fortawesome/fontawesome-free-solid/shakable.es'
import {
faGithub
} from '@fortawesome/fontawesome-free-brands/shakable.es'
fontawesome.library.add(
faUser, faLock, faSignOutAlt, faCog, faGithub
)
Vue.component('fa', FontAwesomeIcon)
import Vue from 'vue'
import store from '~/store'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: 'en',
messages: {}
})
/**
* @param {String} locale
*/
export async function loadMessages (locale) {
if (Object.keys(i18n.getLocaleMessage(locale)).length === 0) {
const messages = await import(/* webpackChunkName: "lang-[request]" */ `~/lang/${locale}`)
i18n.setLocaleMessage(locale, messages)
}
if (i18n.locale !== locale) {
i18n.locale = locale
}
}
;(async function () {
await loadMessages(store.getters['lang/locale'])
})()
export default i18n
import './axios'
import './fontawesome'
import 'bootstrap'
import Vue from 'vue'
import store from '~/store'
import Meta from 'vue-meta'
import routes from './routes'
import Router from 'vue-router'
import { sync } from 'vuex-router-sync'
Vue.use(Meta)
Vue.use(Router)
// The middleware for every page of the application.
const globalMiddleware = ['locale', 'check-auth']
// Load middleware modules dynamically.
const routeMiddleware = resolveMiddleware(
require.context('~/middleware', false, /.*\.js$/)
)
const router = createRouter()
sync(store, router)
export default router
/**
* Create a new router instance.
*
* @return {Router}
*/
function createRouter () {
const router = new Router({
scrollBehavior,
mode: 'history',
routes
})
router.beforeEach(beforeEach)
router.afterEach(afterEach)
return router
}
/**
* Global router guard.
*
* @param {Route} to
* @param {Route} from
* @param {Function} next
*/
async function beforeEach (to, from, next) {
// Get the matched components and resolve them.
const components = await resolveComponents(
router.getMatchedComponents({ ...to })
)
if (components.length === 0) {
return next()
}
// Start the loading bar.
if (components[components.length - 1].loading !== false) {
router.app.$nextTick(() => router.app.$loading.start())
}
// Get the middleware for all the matched components.
const middleware = getMiddleware(components)
// Call each middleware.
callMiddleware(middleware, to, from, (...args) => {
// Set the application layout only if "next()" was called with no args.
if (args.length === 0) {
router.app.setLayout(components[0].layout || '')
}
next(...args)
})
}
/**
* Global after hook.
*
* @param {Route} to
* @param {Route} from
* @param {Function} next
*/
async function afterEach (to, from, next) {
await router.app.$nextTick()
router.app.$loading.finish()
}
/**
* Call each middleware.
*
* @param {Array} middleware
* @param {Route} to
* @param {Route} from
* @param {Function} next
*/
function callMiddleware (middleware, to, from, next) {
const stack = middleware.reverse()
const _next = (...args) => {
// Stop if "_next" was called with an argument or the stack is empty.
if (args.length > 0 || stack.length === 0) {
if (args.length > 0) {
router.app.$loading.finish()
}
return next(...args)
}
const middleware = stack.pop()
if (typeof middleware === 'function') {
middleware(to, from, _next)
} else if (routeMiddleware[middleware]) {
routeMiddleware[middleware](to, from, _next)
} else {
throw Error(`Undefined middleware [${middleware}]`)
}
}
_next()
}
/**
* Resolve async components.
*
* @param {Array} components
* @return {Array}
*/
function resolveComponents (components) {
return Promise.all(components.map(component => {
return typeof component === 'function' ? component() : component
}))
}
/**
* Merge the the global middleware with the components middleware.
*
* @param {Array} components
* @return {Array}
*/
function getMiddleware (components) {
const middleware = [...globalMiddleware]
components.filter(c => c.middleware).forEach(component => {
if (Array.isArray(component.middleware)) {
middleware.push(...component.middleware)
} else {
middleware.push(component.middleware)
}
})
return middleware
}
/**
* Scroll Behavior
*
* @link https://router.vuejs.org/en/advanced/scroll-behavior.html
*
* @param {Route} to
* @param {Route} from
* @param {Object|undefined} savedPosition
* @return {Object}
*/
function scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
const [component] = router.getMatchedComponents({ ...to }).slice(-1)
if (component && component.scrollToTop === false) {
return {}
}
return { x: 0, y: 0 }
}
/**
* @param {Object} requireContext
* @return {Object}
*/
function resolveMiddleware (requireContext) {
return requireContext.keys()
.map(file =>
[file.replace(/(^.\/)|(\.js$)/g, ''), requireContext(file)]
)
.reduce((guards, [name, guard]) => (
{ ...guards, [name]: guard.default }
), {})
}
const Welcome = () => import('~/pages/welcome').then(m => m.default || m)
const Login = () => import('~/pages/auth/login').then(m => m.default || m)
const Register = () => import('~/pages/auth/register').then(m => m.default || m)
const PasswordEmail = () => import('~/pages/auth/password/email').then(m => m.default || m)
const PasswordReset = () => import('~/pages/auth/password/reset').then(m => m.default || m)
const NotFound = () => import('~/pages/errors/404').then(m => m.default || m)
const Home = () => import('~/pages/home').then(m => m.default || m)
const HookAdd = () => import('~/pages/hook/add').then(m => m.default || m)
const HookEdit = () => import('~/pages/hook/edit').then(m => m.default || m)
const HookLog = () => import('~/pages/hook/log').then(m => m.default || m)
const Settings = () => import('~/pages/settings/index').then(m => m.default || m)
const SettingsProfile = () => import('~/pages/settings/profile').then(m => m.default || m)
const SettingsPassword = () => import('~/pages/settings/password').then(m => m.default || m)
export default [
{ path: '/', name: 'welcome', component: Welcome },
{ path: '/login', name: 'login', component: Login },
{ path: '/register', name: 'register', component: Register },
{ path: '/password/reset', name: 'password.request', component: PasswordEmail },
{ path: '/password/reset/:token', name: 'password.reset', component: PasswordReset },
{ path: '/home', name: 'home', component: Home },
{ path: '/add', name: 'hook.add', component: HookAdd},
{ path: '/edit/:id', name: 'hook.edit', component: HookEdit},
{ path: '/log/:id', name: 'hook.log', component: HookLog},
{ path: '/settings',
component: Settings,
children: [
{ path: '', redirect: { name: 'settings.profile' } },
{ path: 'profile', name: 'settings.profile', component: SettingsProfile },
{ path: 'password', name: 'settings.password', component: SettingsPassword }
] },
{ path: '*', component: NotFound }
]
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// Load store modules dynamically.
const requireContext = require.context('./modules', false, /.*\.js$/)
const modules = requireContext.keys()
.map(file =>
[file.replace(/(^.\/)|(\.js$)/g, ''), requireContext(file)]
)
.reduce((modules, [name, module]) => {
if (module.namespaced === undefined) {
module.namespaced = true
}
return { ...modules, [name]: module }
}, {})
export default new Vuex.Store({
modules
})
import axios from 'axios'
import Cookies from 'js-cookie'
import * as types from '../mutation-types'
// state
export const state = {
user: null,
token: Cookies.get('token')
}
// getters
export const getters = {
user: state => state.user,
token: state => state.token,
check: state => state.user !== null
}
// mutations
export const mutations = {
[types.SAVE_TOKEN] (state, { token, remember }) {
state.token = token
Cookies.set('token', token, { expires: remember ? 365 : null })
},
[types.FETCH_USER_SUCCESS] (state, { user }) {
state.user = user
},
[types.FETCH_USER_FAILURE] (state) {
state.token = null
Cookies.remove('token')
},
[types.LOGOUT] (state) {
state.user = null
state.token = null
Cookies.remove('token')
},
[types.UPDATE_USER] (state, { user }) {
state.user = user
}
}
// actions
export const actions = {
saveToken ({ commit, dispatch }, payload) {
commit(types.SAVE_TOKEN, payload)
},
async fetchUser ({ commit }) {
try {
const { data } = await axios.get('/api/user')
commit(types.FETCH_USER_SUCCESS, { user: data })
} catch (e) {
commit(types.FETCH_USER_FAILURE)
}
},
updateUser ({ commit }, payload) {
commit(types.UPDATE_USER, payload)
},
async logout ({ commit }) {
try {
await axios.post('/api/logout')
} catch (e) { }
commit(types.LOGOUT)
},
async fetchOauthUrl (ctx, { provider }) {
const { data } = await axios.post(`/api/oauth/${provider}`)
return data.url
}
}
import axios from 'axios'
import * as types from '../mutation-types'
// state
export const state = {
hooks:[],
hooksById:{}
}
// getters
export const getters = {
hooks: state => state.hooks,
hooksById: state => state.hooksById
}
// mutations
export const mutations = {
[types.FETCH_HOOK] (state, { hooks,hooksById }) {
state.hooks = hooks
state.hooksById = hooksById
}
}
// actions
export const actions = {
async fetchHook ({ commit }) {
try {
const { data } = await axios.get('/api/hook')
commit(types.FETCH_HOOK, { hooks: data.data,hooksById:data.dataById })
} catch (e) {
commit(types.FETCH_HOOK, { hooks: [],hooksById:{}})
}
}
}
import Cookies from 'js-cookie'
import * as types from '../mutation-types'
const { locale, locales } = window.config
// state
export const state = {
locale: Cookies.get('locale') || locale,
locales: locales
}
// getters
export const getters = {
locale: state => state.locale,
locales: state => state.locales
}
// mutations
export const mutations = {
[types.SET_LOCALE] (state, { locale }) {
state.locale = locale
}
}
// actions
export const actions = {
setLocale ({ commit }, { locale }) {
commit(types.SET_LOCALE, { locale })
Cookies.set('locale', locale, { expires: 365 })
}
}
// auth.js
export const LOGOUT = 'LOGOUT'
export const SAVE_TOKEN = 'SAVE_TOKEN'
export const FETCH_USER = 'FETCH_USER'
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'
export const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE'
export const UPDATE_USER = 'UPDATE_USER'
// lang.js
export const SET_LOCALE = 'SET_LOCALE'
export const FETCH_HOOK = 'FETCH_HOOK'
// // Body
$body-bg: #f7f9fb;
// Cards
$card-spacer-x: 0.9375rem;
$card-spacer-y: 0.625rem;
$card-cap-bg: #fbfbfb;
$card-border-color: #e8eced;
// Borders
$border-radius: .125rem;
$border-radius-lg: .2rem;
$border-radius-sm: .15rem;
// Nav Pills
$nav-pills-border-radius: 0;
@import 'variables';
@import '~bootstrap/scss/bootstrap';
@import '~sweetalert2/src/sweetalert2';
@import 'elements/card';
@import 'elements/navbar';
@import 'elements/buttons';
@import 'elements/transitions';
.btn-loading {
position: relative;
pointer-events: none;
color: transparent !important;
&:after {
animation: spinAround 500ms infinite linear;
border: 2px solid #dbdbdb;
border-radius: 50%;
border-right-color: transparent;
border-top-color: transparent;
content: "";
display: block;
height: 1em;
width: 1em;
position: absolute;
left: calc(50% - (1em / 2));
top: calc(50% - (1em / 2));
}
}
@keyframes spinAround {
from { transform: rotate(0deg); }
to { transform: rotate(359deg); }
}
.card {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
}
.navbar {
font-weight: 600;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
}
.nav-item {
.dropdown-menu {
border: none;
margin-top: .5rem;
border-top: 1px solid #f2f2f2 !important;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.12), 0 2px 4px 0 rgba(0,0,0,0.08);
}
}
.nav-link {
.svg-inline--fa {
font-size: 1.4rem;
}
}
.page-enter-active,
.page-leave-active {
transition: opacity .2s;
}
.page-enter,
.page-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .15s
}
.fade-enter,
.fade-leave-to {
opacity: 0
}
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Previous',
'next' => 'Next &raquo;',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'Passwords must be at least six characters and match the confirmation.',
'reset' => 'Your password has been reset!',
'sent' => 'We have e-mailed your password reset link!',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that e-mail address.",
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute must be accepted.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'numeric' => 'The :attribute must be between :min and :max.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'string' => 'The :attribute must be between :min and :max characters.',
'array' => 'The :attribute must have between :min and :max items.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'max' => [
'numeric' => 'The :attribute may not be greater than :max.',
'file' => 'The :attribute may not be greater than :max kilobytes.',
'string' => 'The :attribute may not be greater than :max characters.',
'array' => 'The :attribute may not have more than :max items.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'numeric' => 'The :attribute must be at least :min.',
'file' => 'The :attribute must be at least :min kilobytes.',
'string' => 'The :attribute must be at least :min characters.',
'array' => 'The :attribute must have at least :min items.',
],
'not_in' => 'The selected :attribute is invalid.',
'numeric' => 'The :attribute must be a number.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values is present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid zone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Estas credenciales no coinciden con nuestros registros.',
'throttle' => 'Demasiados intentos de acceso. Por favor intente nuevamente en :seconds segundos.',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Anterior',
'next' => 'Siguiente &raquo;',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reminder Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'Las contraseñas deben coincidir y contener al menos 6 caracteres',
'reset' => '¡Tu contraseña ha sido restablecida!',
'sent' => '¡Te hemos enviado por correo el enlace para restablecer tu contraseña!',
'token' => 'El token de recuperación de contraseña es inválido.',
'user' => 'No podemos encontrar ningún usuario con ese correo electrónico.',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages.
|
*/
'accepted' => ':attribute debe ser aceptado.',
'active_url' => ':attribute no es una URL válida.',
'after' => ':attribute debe ser una fecha posterior a :date.',
'after_or_equal' => ':attribute debe ser una fecha posterior o igual a :date.',
'alpha' => ':attribute sólo debe contener letras.',
'alpha_dash' => ':attribute sólo debe contener letras, números y guiones.',
'alpha_num' => ':attribute sólo debe contener letras y números.',
'array' => ':attribute debe ser un conjunto.',
'before' => ':attribute debe ser una fecha anterior a :date.',
'before_or_equal' => ':attribute debe ser una fecha anterior o igual a :date.',
'between' => [
'numeric' => ':attribute tiene que estar entre :min - :max.',
'file' => ':attribute debe pesar entre :min - :max kilobytes.',
'string' => ':attribute tiene que tener entre :min - :max caracteres.',
'array' => ':attribute tiene que tener entre :min - :max ítems.',
],
'boolean' => 'El campo :attribute debe tener un valor verdadero o falso.',
'confirmed' => 'La confirmación de :attribute no coincide.',
'date' => ':attribute no es una fecha válida.',
'date_format' => ':attribute no corresponde al formato :format.',
'different' => ':attribute y :other deben ser diferentes.',
'digits' => ':attribute debe tener :digits dígitos.',
'digits_between' => ':attribute debe tener entre :min y :max dígitos.',
'dimensions' => 'Las dimensiones de la imagen :attribute no son válidas.',
'distinct' => 'El campo :attribute contiene un valor duplicado.',
'email' => ':attribute no es un correo válido',
'exists' => ':attribute es inválido.',
'file' => 'El campo :attribute debe ser un archivo.',
'filled' => 'El campo :attribute es obligatorio.',
'image' => ':attribute debe ser una imagen.',
'in' => ':attribute es inválido.',
'in_array' => 'El campo :attribute no existe en :other.',
'integer' => ':attribute debe ser un número entero.',
'ip' => ':attribute debe ser una dirección IP válida.',
'ipv4' => ':attribute debe ser un dirección IPv4 válida',
'ipv6' => ':attribute debe ser un dirección IPv6 válida.',
'json' => 'El campo :attribute debe tener una cadena JSON válida.',
'max' => [
'numeric' => ':attribute no debe ser mayor a :max.',
'file' => ':attribute no debe ser mayor que :max kilobytes.',
'string' => ':attribute no debe ser mayor que :max caracteres.',
'array' => ':attribute no debe tener más de :max elementos.',
],
'mimes' => ':attribute debe ser un archivo con formato: :values.',
'mimetypes' => ':attribute debe ser un archivo con formato: :values.',
'min' => [
'numeric' => 'El tamaño de :attribute debe ser de al menos :min.',
'file' => 'El tamaño de :attribute debe ser de al menos :min kilobytes.',
'string' => ':attribute debe contener al menos :min caracteres.',
'array' => ':attribute debe tener al menos :min elementos.',
],
'not_in' => ':attribute es inválido.',
'numeric' => ':attribute debe ser numérico.',
'present' => 'El campo :attribute debe estar presente.',
'regex' => 'El formato de :attribute es inválido.',
'required' => 'El campo :attribute es obligatorio.',
'required_if' => 'El campo :attribute es obligatorio cuando :other es :value.',
'required_unless' => 'El campo :attribute es obligatorio a menos que :other esté en :values.',
'required_with' => 'El campo :attribute es obligatorio cuando :values está presente.',
'required_with_all' => 'El campo :attribute es obligatorio cuando :values está presente.',
'required_without' => 'El campo :attribute es obligatorio cuando :values no está presente.',
'required_without_all' => 'El campo :attribute es obligatorio cuando ninguno de :values estén presentes.',
'same' => ':attribute y :other deben coincidir.',
'size' => [
'numeric' => 'El tamaño de :attribute debe ser :size.',
'file' => 'El tamaño de :attribute debe ser :size kilobytes.',
'string' => ':attribute debe contener :size caracteres.',
'array' => ':attribute debe contener :size elementos.',
],
'string' => 'El campo :attribute debe ser una cadena de caracteres.',
'timezone' => 'El :attribute debe ser una zona válida.',
'unique' => ':attribute ya ha sido registrado.',
'uploaded' => 'Subir :attribute ha fallado.',
'url' => 'El formato :attribute es inválido.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'password' => [
'min' => 'La :attribute debe contener más de :min caracteres',
],
'email' => [
'unique' => 'El :attribute ya ha sido registrado.',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [
'name' => 'nombre',
'username' => 'usuario',
'email' => 'correo electrónico',
'first_name' => 'nombre',
'last_name' => 'apellido',
'password' => 'contraseña',
'password_confirmation' => 'confirmación de la contraseña',
'city' => 'ciudad',
'country' => 'país',
'address' => 'dirección',
'phone' => 'teléfono',
'mobile' => 'móvil',
'age' => 'edad',
'sex' => 'sexo',
'gender' => 'género',
'year' => 'año',
'month' => 'mes',
'day' => 'día',
'hour' => 'hora',
'minute' => 'minuto',
'second' => 'segundo',
'title' => 'título',
'content' => 'contenido',
'body' => 'contenido',
'description' => 'descripción',
'excerpt' => 'extracto',
'date' => 'fecha',
'time' => 'hora',
'subject' => 'asunto',
'message' => 'mensaje',
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => '用户名或手机号与密码不匹配或用户被禁用',
'throttle' => '失败次数太多,请在:seconds秒后再尝试',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; 上一页',
'next' => '下一页 &raquo;',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => '密码长度至少包含6个字符并且两次输入密码要一致',
'reset' => '密码已经被重置!',
'sent' => '我们已经发送密码重置链接到您的邮箱',
'token' => '密码重置令牌无效',
'user' => '抱歉,该邮箱对应的用户不存在!',
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'unique' => ':attribute 已存在',
'accepted' => ':attribute 是被接受的',
'active_url' => ':attribute 必须是一个合法的 URL',
'after' => ':attribute 必须是 :date 之后的一个日期',
'alpha' => ':attribute 必须全部由字母字符构成。',
'alpha_dash' => ':attribute 必须全部由字母、数字、中划线或下划线字符构成',
'alpha_num' => ':attribute 必须全部由字母和数字构成',
'array' => ':attribute 必须是个数组',
'before' => ':attribute 必须是 :date 之前的一个日期',
'between' => [
'numeric' => ':attribute 必须在 :min 到 :max 之间',
'file' => ':attribute 必须在 :min 到 :max KB之间',
'string' => ':attribute 必须在 :min 到 :max 个字符之间',
'array' => ':attribute 必须在 :min 到 :max 项之间',
],
'boolean' => ':attribute 字符必须是 true 或 false',
'confirmed' => ':attribute 两次确认不匹配',
'date' => ':attribute 必须是一个合法的日期',
'date_format' => ':attribute 与给定的格式 :format 不符合',
'different' => ':attribute 必须不同于:other',
'digits' => ':attribute 必须是 :digits 位',
'digits_between' => ':attribute 必须在 :min and :max 位之间',
'email' => ':attribute 必须是一个合法的电子邮件地址。',
'filled' => ':attribute 的字段是必填的',
'exists' => '选定的 :attribute 是无效的',
'image' => ':attribute 必须是一个图片 (jpeg, png, bmp 或者 gif)',
'in' => '选定的 :attribute 是无效的',
'integer' => ':attribute 必须是个整数',
'ip' => ':attribute 必须是一个合法的 IP 地址。',
'max' => [
'numeric' => ':attribute 的最大长度为 :max 位',
'file' => ':attribute 的最大为 :max',
'string' => ':attribute 的最大长度为 :max 字符',
'array' => ':attribute 的最大个数为 :max 个',
],
'mimes' => ':attribute 的文件类型必须是:values',
'mimetypes' => ':attribute 的文件类型必须是: :values.',
'min' => [
'numeric' => ':attribute 的最小长度为 :min 位',
'string' => ':attribute 的最小长度为 :min 字符',
'file' => ':attribute 大小至少为:min KB',
'array' => ':attribute 至少有 :min 项',
],
'not_in' => '选定的 :attribute 是无效的',
'numeric' => ':attribute 必须是数字',
'regex' => ':attribute 格式是无效的',
'required' => ':attribute 字段必须填写',
'required_if' => ':attribute 字段是必须的当 :other 是 :value',
'required_with' => ':attribute 字段是必须的当 :values 是存在的',
'required_with_all' => ':attribute 字段是必须的当 :values 是存在的',
'required_without' => ':attribute 字段是必须的当 :values 是不存在的',
'required_without_all' => ':attribute 字段是必须的当 没有一个 :values 是存在的',
'same' => ':attribute 和 :other 必须匹配',
'size' => [
'numeric' => ':attribute 必须是 :size 位',
'file' => ':attribute 必须是 :size KB',
'string' => ':attribute 必须是 :size 个字符',
'array' => ':attribute 必须包括 :size 项',
],
'url' => ':attribute 无效的格式',
'timezone' => ':attribute 必须个有效的时区',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
];
{{-- Illuminate/Foundation/Exceptions/views --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title')</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Raleway', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.content {
text-align: center;
}
.title {
font-size: 36px;
padding: 20px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
<div class="content">
<div class="title">
@yield('message')
</div>
</div>
</div>
</body>
</html>
@php
$config = [
'appName' => config('app.name'),
'locale' => $locale = app()->getLocale(),
'locales' => config('app.locales'),
'githubAuth' => config('services.github.client_id'),
];
$polyfills = [
'Promise',
'Object.assign',
'Object.values',
'Array.prototype.find',
'Array.prototype.findIndex',
'Array.prototype.includes',
'String.prototype.includes',
'String.prototype.startsWith',
'String.prototype.endsWith',
];
@endphp
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{{ config('app.name') }}</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
<div id="app"></div>
{{-- Global configuration object --}}
<script>window.config = @json($config);</script>
{{-- Polyfill JS features via polyfill.io --}}
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features={{ implode(',', $polyfills) }}"></script>
{{-- Load the application scripts --}}
@if (app()->isLocal())
<script src="{{ mix('js/app.js') }}"></script>
@else
<script src="{{ mix('js/manifest.js') }}"></script>
<script src="{{ mix('js/vendor.js') }}"></script>
<script src="{{ mix('js/app.js') }}"></script>
@endif
</body>
</html>
<html>
<head>
<meta charset="utf-8">
<title>{{ config('app.name') }}</title>
<script>
window.opener.postMessage({ token: "{{ $token }}" }, "{{ url('/') }}")
window.close()
</script>
</head>
<body>
</body>
</html>
@extends('errors.layout')
@section('title', 'Login Error')
@section('message', 'Email already taken.')
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(['middleware' => 'auth:api'], function () {
Route::post('logout', 'Auth\LoginController@logout');
Route::get('/user', function (Request $request) {
return $request->user();
});
Route::patch('settings/profile', 'Settings\ProfileController@update');
Route::patch('settings/password', 'Settings\PasswordController@update');
Route::get('hook', 'Admin\HookController@list');
Route::post('log/{id}', 'Admin\HookLogController@list')->where("id",'(\d+)');
Route::post('hook/add', 'Admin\HookController@add');
Route::post('hook/edit/{id}', 'Admin\HookController@edit')->where("id",'(\d+)');
});
Route::group(['middleware' => 'guest:api'], function () {
Route::post('login', 'Auth\LoginController@login');
Route::post('register', 'Auth\RegisterController@register');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::post('oauth/{driver}', 'Auth\OAuthController@redirectToProvider');
Route::get('oauth/{driver}/callback', 'Auth\OAuthController@handleProviderCallback')->name('oauth.callback');
});
<?php
/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
<?php
use Illuminate\Foundation\Inspiring;
/*
|--------------------------------------------------------------------------
| Console Routes
|--------------------------------------------------------------------------
|
| This file is where you may define all of your Closure based console
| commands. Each Closure is bound to a command instance allowing a
| simple approach to interacting with each command's IO methods.
|
*/
Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->describe('Display an inspiring quote');
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::any("hook/{id}", "HookController@index");
Route::get('{path}', function () {
return view('index');
})->where('path', '(.*)');
<?php
/**
* Laravel - A PHP Framework For Web Artisans.
*
* @author Taylor Otwell <taylor@laravel.com>
*/
$uri = urldecode(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);
// This file allows us to emulate Apache's "mod_rewrite" functionality from the
// built-in PHP web server. This provides a convenient way to test a Laravel
// application without having installed a "real" web server software here.
if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
return false;
}
require_once __DIR__.'/public/index.php';
*
!public/
!.gitignore
config.php
routes.php
schedule-*
compiled.php
services.json
events.scanned.php
routes.scanned.php
down
<?php
namespace Tests\Browser;
use App\User;
use Tests\DuskTestCase;
use Tests\Browser\Pages\Home;
use Tests\Browser\Pages\Login;
class LoginTest extends DuskTestCase
{
public function setUp()
{
parent::setup();
static::closeAll();
}
/** @test */
public function login_with_valid_credentials()
{
$user = factory(User::class)->create();
$this->browse(function ($browser) use ($user) {
$browser->visit(new Login)
->submit($user->email, 'secret')
->assertPageIs(Home::class);
});
}
/** @test */
public function login_with_invalid_credentials()
{
$this->browse(function ($browser) {
$browser->visit(new Login)
->submit('test@test.app', 'secret')
->assertSee('These credentials do not match our records.');
});
}
/** @test */
public function log_out_the_user()
{
$user = factory(User::class)->create();
$this->browse(function ($browser) use ($user) {
$browser->visit(new Login)
->submit($user->email, 'secret')
->on(new Home)
->clickLogout()
->assertPageIs(Login::class);
});
}
}
<?php
namespace Tests\Browser\Pages;
class Home extends Page
{
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/home';
}
/**
* Click on the log out link.
*
* @param \Laravel\Dusk\Browser $browser
* @return void
*/
public function clickLogout($browser)
{
$browser->clickLink('Logout')
->pause(300);
}
}
<?php
namespace Tests\Browser\Pages;
class Login extends Page
{
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/login';
}
/**
* Submit the form with the given credentials.
*
* @param \Laravel\Dusk\Browser $browser
* @param string $email
* @param string $password
* @return void
*/
public function submit($browser, $email, $password)
{
$browser->type('email', $email)
->type('password', $password)
->press('Log In')
->pause(350);
}
}
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Page as BasePage;
abstract class Page extends BasePage
{
/**
* Assert that the browser is on the page.
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertPathIs($this->url());
}
/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
}
<?php
namespace Tests\Browser\Pages;
class Register extends Page
{
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/register';
}
/**
* Submit the form with the given data.
*
* @param \Laravel\Dusk\Browser $browser
* @param array $data
* @return void
*/
public function submit($browser, array $data = [])
{
foreach ($data as $key => $value) {
$browser->type($key, $value);
}
$browser->press('Register')
->pause(500);
}
}
<?php
namespace Tests\Browser;
use App\User;
use Tests\DuskTestCase;
use Tests\Browser\Pages\Home;
use Tests\Browser\Pages\Register;
class RegisterTest extends DuskTestCase
{
public function setUp()
{
parent::setup();
static::closeAll();
}
/** @test */
public function register_with_valid_data()
{
$this->browse(function ($browser) {
$browser->visit(new Register)
->submit([
'name' => 'Test User',
'email' => 'test@test.app',
'password' => 'secret',
'password_confirmation' => 'secret',
])
->assertPageIs(Home::class);
});
}
/** @test */
public function can_not_register_with_the_same_twice()
{
$user = factory(User::class)->create();
$this->browse(function ($browser) use ($user) {
$browser->visit(new Register)
->submit([
'name' => 'Test User',
'email' => $user->email,
'password' => 'secret',
'password_confirmation' => 'secret',
])
->assertSee('The email has already been taken.');
});
}
}
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
class WelcomeTest extends DuskTestCase
{
/** @test */
public function basic_test()
{
$this->browse(function ($browser) {
$browser->visit('/')
->assertSee('Laravel');
});
}
}
<?php
namespace Tests;
use Illuminate\Support\Facades\Hash;
use Illuminate\Contracts\Console\Kernel;
trait CreatesApplication
{
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
Hash::setRounds(4);
return $app;
}
}
<?php
namespace Tests;
use Laravel\Dusk\Page;
use Laravel\Dusk\Browser;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Illuminate\Foundation\Testing\DatabaseMigrations;
Browser::macro('assertPageIs', function ($page) {
if (! $page instanceof Page) {
$page = new $page;
}
return $this->assertPathIs($page->url());
});
abstract class DuskTestCase extends BaseTestCase
{
use DatabaseMigrations;
use CreatesApplication;
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
static::startChromeDriver();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
]);
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}
}
<?php
namespace Tests\Feature;
use Tests\TestCase;
class LocaleTest extends TestCase
{
/** @test */
public function set_locale_from_header()
{
$this->withHeaders(['Accept-Language' => 'zh-CN'])
->postJson('/api/login');
$this->assertEquals('zh-CN', $this->app->getLocale());
}
/** @test */
public function set_locale_from_header_short()
{
$this->withHeaders(['Accept-Language' => 'en-US'])
->postJson('/api/login');
$this->assertEquals('en', $this->app->getLocale());
}
}
<?php
namespace Tests\Feature;
use App\User;
use Tests\TestCase;
class LoginTest extends TestCase
{
/** @var \App\User */
protected $user;
public function setUp()
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/** @test */
public function authenticate()
{
$this->postJson('/api/login', [
'email' => $this->user->email,
'password' => 'secret',
])
->assertSuccessful()
->assertJsonStructure(['token', 'expires_in'])
->assertJson(['token_type' => 'bearer']);
}
/** @test */
public function fetch_the_current_user()
{
$this->actingAs($this->user)
->getJson('/api/user')
->assertSuccessful()
->assertJsonStructure(['id', 'name', 'email']);
}
/** @test */
public function log_out()
{
$token = $this->postJson('/api/login', [
'email' => $this->user->email,
'password' => 'secret',
])->json()['token'];
$this->postJson("/api/logout?token=$token")
->assertSuccessful();
$this->getJson("/api/user?token=$token")
->assertStatus(401);
}
}
<?php
namespace Tests\Feature;
use App\User;
use Mockery as m;
use Tests\TestCase;
use Laravel\Socialite\Facades\Socialite;
use PHPUnit\Framework\Assert as PHPUnit;
use Illuminate\Foundation\Testing\TestResponse;
use Laravel\Socialite\Two\User as SocialiteUser;
class OAuthTest extends TestCase
{
public function setUp()
{
parent::setUp();
TestResponse::macro('assertText', function ($text) {
PHPUnit::assertTrue(str_contains($this->getContent(), $text), "Expected text [{$text}] not found.");
return $this;
});
TestResponse::macro('assertTextMissing', function ($text) {
PHPUnit::assertFalse(str_contains($this->getContent(), $text), "Expected missing text [{$text}] found.");
return $this;
});
}
/** @test */
public function redirect_to_provider()
{
$this->mockSocialite('github');
$this->postJson('/api/oauth/github')
->assertSuccessful()
->assertJson(['url' => 'https://url-to-provider']);
}
/** @test */
public function create_user_and_return_token()
{
$this->mockSocialite('github', [
'id' => '123',
'name' => 'Test User',
'email' => 'test@example.com',
'token' => 'access-token',
'refreshToken' => 'refresh-token',
]);
$this->withoutExceptionHandling();
$this->get('/api/oauth/github/callback')
->assertText('token')
->assertSuccessful();
$this->assertDatabaseHas('users', [
'name' => 'Test User',
'email' => 'test@example.com',
]);
$this->assertDatabaseHas('oauth_providers', [
'user_id' => User::first()->id,
'provider' => 'github',
'provider_user_id' => '123',
'access_token' => 'access-token',
'refresh_token' => 'refresh-token',
]);
}
/** @test */
public function update_user_and_return_token()
{
$user = factory(User::class)->create(['email' => 'test@example.com']);
$user->oauthProviders()->create([
'provider' => 'github',
'provider_user_id' => '123',
]);
$this->mockSocialite('github', [
'id' => '123',
'email' => 'test@example.com',
'token' => 'updated-access-token',
'refreshToken' => 'updated-refresh-token',
]);
$this->get('/api/oauth/github/callback')
->assertText('token')
->assertSuccessful();
$this->assertDatabaseHas('oauth_providers', [
'user_id' => $user->id,
'access_token' => 'updated-access-token',
'refresh_token' => 'updated-refresh-token',
]);
}
/** @test */
public function can_not_create_user_if_email_is_taken()
{
factory(User::class)->create(['email' => 'test@example.com']);
$this->mockSocialite('github', ['email' => 'test@example.com']);
$this->get('/api/oauth/github/callback')
->assertText('Email already taken.')
->assertTextMissing('token')
->assertStatus(400);
}
protected function mockSocialite($provider, $user = null)
{
$mock = Socialite::shouldReceive('stateless')
->andReturn(m::self())
->shouldReceive('driver')
->with($provider)
->andReturn(m::self());
if ($user) {
$mock->shouldReceive('user')
->andReturn((new SocialiteUser)->setRaw($user)->map($user));
} else {
$mock->shouldReceive('redirect')
->andReturn(redirect('https://url-to-provider'));
}
}
}
<?php
namespace Tests\Feature;
use Tests\TestCase;
class RegisterTest extends TestCase
{
/** @test */
public function can_register()
{
$this->postJson('/api/register', [
'name' => 'Test User',
'email' => 'test@test.app',
'password' => 'secret',
'password_confirmation' => 'secret',
])
->assertSuccessful()
->assertJsonStructure(['id', 'name', 'email']);
}
}
<?php
namespace Tests\Feature;
use App\User;
use Tests\TestCase;
use Illuminate\Support\Facades\Hash;
class SettingsTest extends TestCase
{
/** @var \App\User */
protected $user;
public function setUp()
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/** @test */
public function update_profile_info()
{
$this->actingAs($this->user)
->patchJson('/api/settings/profile', [
'name' => 'Test User',
'email' => 'test@test.app',
])
->assertSuccessful()
->assertJsonStructure(['id', 'name', 'email']);
$this->assertDatabaseHas('users', [
'id' => $this->user->id,
'name' => 'Test User',
'email' => 'test@test.app',
]);
}
/** @test */
public function update_password()
{
$this->actingAs($this->user)
->patchJson('/api/settings/password', [
'password' => 'updated',
'password_confirmation' => 'updated',
])
->assertSuccessful();
$this->assertTrue(Hash::check('updated', $this->user->password));
}
}
<?php
namespace Tests;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use RefreshDatabase;
use CreatesApplication;
}
<?php
namespace Tests\Unit;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}
const path = require('path')
const mix = require('laravel-mix')
// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
mix.config.vue.esModule = true
mix
.js('resources/assets/js/app.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css')
.sourceMaps()
.disableNotifications()
if (mix.inProduction()) {
mix.version()
mix.extract([
'vue',
'vform',
'axios',
'vuex',
'jquery',
'popper.js',
'vue-i18n',
'vue-meta',
'js-cookie',
'bootstrap',
'vue-router',
'sweetalert2',
'vuex-router-sync',
'@fortawesome/fontawesome',
'@fortawesome/vue-fontawesome'
])
}
mix.webpackConfig({
plugins: [
// new BundleAnalyzerPlugin()
],
resolve: {
extensions: ['.js', '.json', '.vue'],
alias: {
'~': path.join(__dirname, './resources/assets/js')
}
// },
// output: {
// chunkFilename: 'js/[name].[chunkhash].js',
// publicPath: mix.config.hmr ? '//localhost:8080' : '/'
// }
})
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment