Version 0.7.0 #1

Merged
kor-elf merged 90 commits from develop into main 2023-12-08 21:18:23 +06:00
10 changed files with 67 additions and 5 deletions
Showing only changes of commit b5db913c24 - Show all commits

View File

@ -4,6 +4,10 @@ APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://localhost APP_URL=http://localhost
APP_DEMO_MODE=false
APP_DEMO_EMAIL=
APP_DEMO_PASSWORD=
APP_DEFAULT_USER_TIMEZONE=UTC APP_DEFAULT_USER_TIMEZONE=UTC
# Valid languages: ru | en # Valid languages: ru | en
APP_DEFAULT_LOCALE=ru APP_DEFAULT_LOCALE=ru

View File

@ -2,6 +2,7 @@
namespace App\Helpers; namespace App\Helpers;
use App\Models\User;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -21,4 +22,18 @@ final readonly class Helpers
public static function getUserTimeZone() { public static function getUserTimeZone() {
return auth()->user()?->timezone ?? config('app.user_timezone'); return auth()->user()?->timezone ?? config('app.user_timezone');
} }
public static function isDemoMode(): bool
{
return config('app.demo_mode', false);
}
public static function isDemoModeAndUserDenyUpdate(User $user): bool
{
if (self::isDemoMode() !== true) {
return false;
}
return $user->email === config('app.demo_email');
}
} }

View File

@ -11,6 +11,7 @@ use App\Captcha\Images\Head;
use App\Captcha\Images\ImageManager; use App\Captcha\Images\ImageManager;
use App\Captcha\Images\Lines; use App\Captcha\Images\Lines;
use App\Contracts\CryptographyContract; use App\Contracts\CryptographyContract;
use App\Helpers\Helpers;
use App\Services\Api\V1\CaptchaGenerateService; use App\Services\Api\V1\CaptchaGenerateService;
use App\Services\CaptchaToken\CaptchaTokenHandler; use App\Services\CaptchaToken\CaptchaTokenHandler;
use App\Services\CryptographyString; use App\Services\CryptographyString;
@ -20,6 +21,7 @@ use App\Services\Search\CreateSearchInstanceCommand;
use App\Services\Search\Search; use App\Services\Search\Search;
use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Foundation\Application;
use Illuminate\Pagination\Paginator; use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Rules\Password; use Illuminate\Validation\Rules\Password;
@ -64,6 +66,10 @@ final class AppServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
Blade::if('demo', function () {
return Helpers::isDemoMode();
});
Password::defaults(function () { Password::defaults(function () {
$rule = Password::min(8); $rule = Password::min(8);

View File

@ -5,6 +5,7 @@ namespace App\Services\Private;
use App\Dto\Request\Private\Profile\Update; use App\Dto\Request\Private\Profile\Update;
use App\Dto\Request\Private\Profile\UpdateSettings; use App\Dto\Request\Private\Profile\UpdateSettings;
use App\Dto\Request\Private\User\UpdatePassword; use App\Dto\Request\Private\User\UpdatePassword;
use App\Helpers\Helpers;
use App\Models\User; use App\Models\User;
use App\ServiceResults\ServiceResultError; use App\ServiceResults\ServiceResultError;
use App\ServiceResults\ServiceResultSuccess; use App\ServiceResults\ServiceResultSuccess;
@ -19,6 +20,10 @@ final class ProfileService extends Service
public function update(Update $update, User $user): ServiceResultError | ServiceResultSuccess public function update(Update $update, User $user): ServiceResultError | ServiceResultSuccess
{ {
if (Helpers::isDemoModeAndUserDenyUpdate($user)) {
return $this->errValidate(__('Demo Mode'));
}
try { try {
$data = [ $data = [
'name' => $update->getName() 'name' => $update->getName()
@ -33,6 +38,10 @@ final class ProfileService extends Service
public function updatePassword(UpdatePassword $update, User $user): ServiceResultError | ServiceResultSuccess public function updatePassword(UpdatePassword $update, User $user): ServiceResultError | ServiceResultSuccess
{ {
if (Helpers::isDemoModeAndUserDenyUpdate($user)) {
return $this->errValidate(__('Demo Mode'));
}
try { try {
$this->userCommandHandler->handleUpdatePassword($user, $update->getPassword()); $this->userCommandHandler->handleUpdatePassword($user, $update->getPassword());
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@ -5,6 +5,7 @@ namespace App\Services\Private;
use App\Dto\Builder\User as UserBuilderDto; use App\Dto\Builder\User as UserBuilderDto;
use App\Dto\Request\Private\User\StoreUpdate; use App\Dto\Request\Private\User\StoreUpdate;
use App\Dto\Request\Private\User\UpdatePassword; use App\Dto\Request\Private\User\UpdatePassword;
use App\Helpers\Helpers;
use App\Models\User; use App\Models\User;
use App\Dto\QuerySettingsDto; use App\Dto\QuerySettingsDto;
use App\Repositories\RoleRepository; use App\Repositories\RoleRepository;
@ -120,6 +121,10 @@ final class UserService extends Service
return $this->errFobidden(__('Access is denied')); return $this->errFobidden(__('Access is denied'));
} }
if (Helpers::isDemoModeAndUserDenyUpdate($modelUser)) {
return $this->errValidate(__('Demo Mode'));
}
if ($this->userRepository->isExistsEmail($data->getEmail(), $modelUser->id)) { if ($this->userRepository->isExistsEmail($data->getEmail(), $modelUser->id)) {
return $this->errValidate( return $this->errValidate(
__('validation.unique', ['attribute' => __('validation.attributes.email')]), __('validation.unique', ['attribute' => __('validation.attributes.email')]),
@ -156,6 +161,10 @@ final class UserService extends Service
return $this->errFobidden(__('Access is denied')); return $this->errFobidden(__('Access is denied'));
} }
if (Helpers::isDemoModeAndUserDenyUpdate($modelUser)) {
return $this->errValidate(__('Demo Mode'));
}
try { try {
$this->userCommandHandler->handleUpdatePassword($modelUser, $data->getPassword()); $this->userCommandHandler->handleUpdatePassword($modelUser, $data->getPassword());
} catch (\Throwable $e) { } catch (\Throwable $e) {
@ -178,6 +187,10 @@ final class UserService extends Service
return $this->errFobidden(__('Access is denied')); return $this->errFobidden(__('Access is denied'));
} }
if (Helpers::isDemoModeAndUserDenyUpdate($modelUser)) {
return $this->errValidate(__('Demo Mode'));
}
try { try {
DB::transaction(function () use ($modelUser) { DB::transaction(function () use ($modelUser) {
$this->userCommandHandler->handleDestroy($modelUser); $this->userCommandHandler->handleDestroy($modelUser);

View File

@ -30,6 +30,18 @@ return [
'env' => env('APP_ENV', 'production'), 'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Demo Mode
|--------------------------------------------------------------------------
|
| Enables or disables Demo mode.
| When Demo mode is enabled, it disables the ability to change user.
*/
'demo_mode' => (bool) env('APP_DEMO_MODE', false),
'demo_email' => env('APP_DEMO_EMAIL', false),
'demo_password' => env('APP_DEMO_PASSWORD', false),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Application Debug Mode | Application Debug Mode

View File

@ -72,5 +72,6 @@
"Captcha User Agent does not match": "The CAPTCHA verification state has been rejected because the User Agent does not match. Please check and ensure that you are using the same User Agent.", "Captcha User Agent does not match": "The CAPTCHA verification state has been rejected because the User Agent does not match. Please check and ensure that you are using the same User Agent.",
"The time for captcha verification has passed": "The time for captcha verification has passed.", "The time for captcha verification has passed": "The time for captcha verification has passed.",
"Captcha does not pass verification": "Captcha does not pass verification.", "Captcha does not pass verification": "Captcha does not pass verification.",
"Add code to the site": "Add code to the site" "Add code to the site": "Add code to the site",
"Demo Mode": "!!! Demo Mode !!!"
} }

View File

@ -72,5 +72,6 @@
"Captcha User Agent does not match": "Отклонено состояние проверки CAPTCHA, так как User Agent не совпадает. Пожалуйста, проверьте и убедитесь, что вы используете тот же User Agent.", "Captcha User Agent does not match": "Отклонено состояние проверки CAPTCHA, так как User Agent не совпадает. Пожалуйста, проверьте и убедитесь, что вы используете тот же User Agent.",
"The time for captcha verification has passed": "Время верификации капчи прошло.", "The time for captcha verification has passed": "Время верификации капчи прошло.",
"Captcha does not pass verification": "Капча не проходит проверку.", "Captcha does not pass verification": "Капча не проходит проверку.",
"Add code to the site": "Добавьте код на сайт" "Add code to the site": "Добавьте код на сайт",
"Demo Mode": "!!! Включён демо режим !!!"
} }

View File

@ -56,7 +56,7 @@
</nav> </nav>
<main class="content min-vh-100 position-relative pb-7 pb-lg-5"> <main class="content min-vh-100 position-relative pb-7 pb-lg-5">
@demo <p style="border: 1px solid #ff1810; color: #ff1810; font-weight: bold; font-size: 18px; padding: 7px; text-align: center;">{{ __('Demo Mode') }}</p> @endif
<nav class="navbar navbar-top navbar-expand navbar-dashboard navbar-dark ps-0 pe-2 pb-2 pb-lg-3"> <nav class="navbar navbar-top navbar-expand navbar-dashboard navbar-dark ps-0 pe-2 pb-2 pb-lg-3">
<div class="container-fluid px-0"> <div class="container-fluid px-0">
<div class="d-flex justify-content-between w-100" id="navbarSupportedContent"> <div class="d-flex justify-content-between w-100" id="navbarSupportedContent">

View File

@ -3,6 +3,7 @@
<div class="row justify-content-center form-bg-image" data-background-lg="{{ Vite::asset('resources/volt/images/illustrations/signin.svg') }}"> <div class="row justify-content-center form-bg-image" data-background-lg="{{ Vite::asset('resources/volt/images/illustrations/signin.svg') }}">
<div class="col-12 d-flex align-items-center justify-content-center"> <div class="col-12 d-flex align-items-center justify-content-center">
<div class="bg-white shadow border-0 rounded border-light p-4 p-lg-5 w-100 fmxw-500"> <div class="bg-white shadow border-0 rounded border-light p-4 p-lg-5 w-100 fmxw-500">
@demo <p style="border: 1px solid #ff1810; color: #ff1810; font-weight: bold; font-size: 18px; padding: 7px; text-align: center;">{{ __('Demo Mode') }}</p> @endif
<div class="text-center text-md-center mb-4 mt-md-0"> <div class="text-center text-md-center mb-4 mt-md-0">
<h1 class="mb-0 h3">{{ __('Sign in to our platform') }}</h1> <h1 class="mb-0 h3">{{ __('Sign in to our platform') }}</h1>
</div> </div>
@ -24,7 +25,7 @@
<span class="input-group-text" id="basic-addon1"> <span class="input-group-text" id="basic-addon1">
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"></path><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path></svg> <svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"></path><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"></path></svg>
</span> </span>
<input type="email" name="email" class="form-control" placeholder="example@company.com" id="email" autofocus required> <input type="email" name="email" class="form-control" placeholder="example@company.com" @demo value="{{ config('app.demo_email', null) }}" @endif id="email" autofocus required>
</div> </div>
</div> </div>
<!-- End of Form --> <!-- End of Form -->
@ -36,7 +37,7 @@
<span class="input-group-text" id="basic-addon2"> <span class="input-group-text" id="basic-addon2">
<svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg> <svg class="icon icon-xs text-gray-600" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg>
</span> </span>
<input type="password" name="password" placeholder="Password" class="form-control" id="password" required> <input type="password" name="password" placeholder="Password" @demo value="{{ config('app.demo_password', null) }}" @endif class="form-control" id="password" required>
</div> </div>
</div> </div>
<!-- End of Form --> <!-- End of Form -->