Merge branch 'develop'

# Conflicts:
#	public/js/10.js
#	public/js/11.js
#	public/js/13.js
#	public/js/14.js
#	public/js/2.js
#	public/js/20.js
#	public/js/21.js
#	public/js/22.js
#	public/js/23.js
#	public/js/3.js
#	public/js/8.js
#	public/js/9.js
#	public/js/app.js
This commit is contained in:
Dennis Smink
2020-09-21 15:06:26 +02:00
29 changed files with 40074 additions and 57 deletions

View File

@@ -1,7 +1,7 @@
APP_NAME=Laravel
APP_ENV=local
APP_ENV=production
APP_KEY=
APP_DEBUG=true
APP_DEBUG=false
APP_URL=http://localhost
APP_DEMO=false

View File

@@ -18,6 +18,8 @@ class SettingController extends Controller
'documentation' => setting('documentation'),
'allow_registration' => setting('allow_registration'),
'default_package' => setting('default_package'),
'enable_api' => setting('enable_api'),
'api_token' => setting('api_token') ? decrypt(setting('api_token')) : null,
];
$packages = Package::pluck('name', 'id');
@@ -37,8 +39,14 @@ class SettingController extends Controller
'support_emails',
'allow_registration',
'documentation',
'default_package'
'default_package',
'enable_api',
'api_token',
]) as $key => $value) {
if ($key === 'api_token') {
$value = encrypt($value);
}
setting([$key => $value]);
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\UserRequest;
use App\Http\Resources\Api\UserResource;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index()
{
return UserResource::collection(User::latest()->paginate());
}
public function store(UserRequest $request)
{
$user = User::create($request->validated());
return new UserResource($user);
}
public function show($id)
{
return new UserResource(User::findOrFail($id));
}
}

View File

@@ -23,6 +23,11 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
protected $middlewarePriority = [
\Inertia\Middleware::class,
\App\Http\Middleware\Demo::class,
];
/**
* The application's route middleware groups.
*
@@ -63,6 +68,7 @@ class Kernel extends HttpKernel
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'global.api.authenticated' => \App\Http\Middleware\GlobalApiAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'role' => \App\Http\Middleware\RoleMiddleware::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Middleware;
use App\Services\Ploi\Exceptions\Http\Unauthenticated;
use Closure;
use Illuminate\Http\Request;
class GlobalApiAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (!$this->isAuthenticated($request)) {
throw new Unauthenticated('Unauthenticated for global access.');
}
return $next($request);
}
protected function isAuthenticated(Request $request)
{
return
setting('enable_api') &&
setting('api_token') &&
$request->bearerToken() &&
$request->bearerToken() === decrypt(setting('api_token'));
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->bearerToken() && $this->bearerToken() === setting('api_token');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => [
'required',
'string',
'max:255'
],
'email' => [
'required',
'string',
'email',
'max:255',
'unique:users'
]
];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Http\Resources\Api;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'avatar' => $this->avatar,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
];
}
}

View File

@@ -29,17 +29,11 @@ class AppServiceProvider extends ServiceProvider
}
});
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->registerInertia();
$this->registerLengthAwarePaginator();
if (!$this->app->request->is('api*')) {
$this->registerInertia();
$this->registerLengthAwarePaginator();
}
}
public function registerInertia()

View File

@@ -2,8 +2,11 @@
namespace App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
@@ -30,35 +33,37 @@ class RouteServiceProvider extends ServiceProvider
*/
public function boot()
{
//
$this->configureRateLimiting();
parent::boot();
$this->routes(function () {
if (setting('enable_api')) {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace . '\Api')
->group(base_path('routes/api.php'));
}
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
Route::middleware(['web', 'auth', 'role:admin'])
->prefix('admin')
->as('admin.')
->namespace($this->namespace . '\\Admin')
->group(base_path('routes/admin.php'));
});
}
/**
* Define the routes for the application.
* Configure the rate limiters for the application.
*
* @return void
*/
public function map()
protected function configureRateLimiting()
{
$this->mapWebRoutes();
$this->mapAdminRoutes();
}
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapAdminRoutes()
{
Route::middleware(['web', 'auth', 'role:admin'])
->prefix('admin')
->as('admin.')
->namespace($this->namespace .'\\Admin')
->group(base_path('routes/admin.php'));
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60);
});
}
}

1854
public/js/10.js vendored

File diff suppressed because one or more lines are too long

1686
public/js/11.js vendored

File diff suppressed because one or more lines are too long

1632
public/js/13.js vendored

File diff suppressed because one or more lines are too long

2076
public/js/14.js vendored

File diff suppressed because one or more lines are too long

885
public/js/2.js vendored

File diff suppressed because one or more lines are too long

1524
public/js/20.js vendored

File diff suppressed because one or more lines are too long

1149
public/js/21.js vendored

File diff suppressed because one or more lines are too long

1345
public/js/22.js vendored

File diff suppressed because one or more lines are too long

1165
public/js/23.js vendored

File diff suppressed because one or more lines are too long

816
public/js/3.js vendored

File diff suppressed because one or more lines are too long

1844
public/js/8.js vendored

File diff suppressed because one or more lines are too long

1912
public/js/9.js vendored

File diff suppressed because one or more lines are too long

21946
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -63,6 +63,18 @@
</p>
</div>
<div>
<input id="enable_api" class="form-checkbox" type="checkbox"
v-model="form.enable_api">
<label for="enable_api" class="ml-2 text-sm">{{ __('Enable API') }}</label>
<p class="text-small mt-1 text-medium-emphasis">
{{ __('This will allow you to interact with your system via the API.') }}
</p>
</div>
<FormInput v-if="form.enable_api" allow-random-string :label="__('API token')" :errors="$page.errors.api_token"
v-model="form.api_token"/>
<FormActions>
<Button>{{ __('Save changes') }}</Button>
</FormActions>
@@ -140,6 +152,8 @@
email: this.company_settings.email,
support_emails: this.company_settings.support_emails,
support: this.company_settings.support,
enable_api: this.company_settings.enable_api,
api_token: this.company_settings.api_token,
documentation: this.company_settings.documentation,
allow_registration: this.company_settings.allow_registration,
default_package: this.company_settings.default_package,
@@ -158,7 +172,9 @@
submit() {
this.sending = true
this.$inertia.patch(this.route('admin.settings.update'), this.form)
this.$inertia.patch(this.route('admin.settings.update'), this.form, {
preserveScroll: true
})
.then(() => {
this.sending = false;
})

View File

@@ -18,9 +18,6 @@
<template #segments>
<SettingsSegment>
<template #title>{{ __('Edit') }}</template>
<template #subtitle>
Form
</template>
<template #form>
<form class="space-y-4" @submit.prevent="submit">
<FormInput :label="__('Name')" :errors="$page.errors.name" v-model="form.name" />

View File

@@ -1,5 +1,5 @@
<template>
<FormGroup class="relative">
<FormGroup class="relative max-w-lg">
<Label :errors="errors" :forId="id">{{ label }}</Label>
<button type="button" @click="copy" v-if="allowCopy" class="flex items-center right-0 absolute text-xs text-medium-emphasis">
@@ -7,6 +7,11 @@
{{ copyText }}
</button>
<button type="button" @click="generateString" v-if="allowRandomString" class="flex items-center right-0 absolute text-xs text-medium-emphasis">
<IconKey class="mr-2" />
{{ __('Generate') }}
</button>
<input :id="id"
:class="[defaultClasses]"
:type="type"
@@ -16,6 +21,7 @@
:placeholder="placeholder" />
<ErrorText v-if="errors">{{ errors[0] }}</ErrorText>
<HelperText v-if="helperText && !errors">{{ helperText }}</HelperText>
</FormGroup>
</template>
@@ -25,9 +31,10 @@ import Label from '@/components/Label'
import ErrorText from '@/components/ErrorText'
import HelperText from '@/components/HelperText'
import IconClipboard from '@/components/icons/IconClipboard'
import IconKey from '@/components/icons/IconKey'
const defaultClasses =
'w-full border-medium-emphasis text-body h-10 max-w-lg px-2 border rounded bg-surface-1 focus:outline-none focus:border-primary'
'w-full border-medium-emphasis text-body h-10 px-2 border rounded bg-surface-1 focus:outline-none focus:border-primary'
export default {
props: {
@@ -64,6 +71,11 @@ export default {
required: false,
default: false,
type: Boolean
},
allowRandomString: {
required: false,
default: false,
type: Boolean
}
},
@@ -73,6 +85,7 @@ export default {
ErrorText,
HelperText,
IconClipboard,
IconKey,
},
data() {
@@ -103,6 +116,9 @@ export default {
this.$copyText(this.value);
},
generateString() {
this.$emit('input', this.randomString());
}
},
computed: {

View File

@@ -0,0 +1,6 @@
<template>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-key" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11 9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4 4 0 0 1 0 8zm4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.793-.793-1-1h-6.63a.5.5 0 0 1-.451-.285A3 3 0 0 0 4 5z"/>
<path d="M4 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
</svg>
</template>

View File

@@ -1,9 +1,9 @@
<template>
<div class="grid grid-cols-4 gap-16">
<aside class="col-span-1">
<aside class="col-span-4 md:col-span-1">
<slot name="nav" />
</aside>
<section class="col-span-3">
<section class="col-span-4 md:col-span-3">
<div class="space-y-16">
<slot name="segments"></slot>
</div>

View File

@@ -24,5 +24,14 @@ module.exports = {
return splitted[0];
},
randomString() {
let m = m || 20;
let s = '', r = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < m; i++) {
s += r.charAt(Math.floor(Math.random() * r.length));
}
return s;
},
}
};

22
routes/api.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| 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' => 'global.api.authenticated'], function () {
Route::group(['prefix' => 'users'], function () {
Route::get('/', 'UserController@index');
Route::post('/', 'UserController@store');
Route::get('{user}', 'UserController@show');
});
});