Redesign the UI

This commit is contained in:
Timothée Jaussoin 2023-06-09 08:24:49 +00:00
parent 02983102c0
commit fc96338bfb
98 changed files with 3069 additions and 1618 deletions

View file

@ -3,7 +3,7 @@ variables:
DEBIAN_11_IMAGE_VERSION: 20230322_172926_missing_tools
PHP_REDIS_REMI_VERSION: php-pecl-redis5-5.3.6-1
PHP_IGBINARY_REMI_VERSION: php-pecl-igbinary-3.2.14-1
PHP_MSGPACK_REMI_VERSION: php-pecl-msgpack-2.1.2-1
PHP_MSGPACK_REMI_VERSION: php-pecl-msgpack-2.2.0-1
PHP_XMLRPC_REMI_VERSION: php-pecl-xmlrpc-1.0.0~rc3-2
include:

View file

@ -21,7 +21,6 @@ namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
@ -29,8 +28,6 @@ use Illuminate\Support\Str;
use App\ApiKey;
use App\Password;
use App\EmailChanged;
use App\Mail\ChangingEmail;
use Carbon\Carbon;
use Illuminate\Http\Request;
@ -38,7 +35,7 @@ class Account extends Authenticatable
{
use HasFactory;
protected $with = ['passwords', 'admin', 'emailChanged', 'alias', 'activationExpiration', 'types', 'actions'];
protected $with = ['passwords', 'admin', 'alias', 'activationExpiration', 'emailChangeCode', 'types', 'actions'];
protected $hidden = ['alias', 'expire_time', 'confirmation_key', 'provisioning_token', 'pivot'];
protected $dateTimes = ['creation_time'];
protected $appends = ['realm', 'phone', 'confirmation_key_expires'];
@ -137,11 +134,6 @@ class Account extends Authenticatable
return $this->belongsToMany(Account::class, 'contacts', 'account_id', 'contact_id');
}
public function emailChanged()
{
return $this->hasOne(EmailChanged::class);
}
public function nonces()
{
return $this->hasMany(DigestNonce::class);
@ -162,6 +154,11 @@ class Account extends Authenticatable
return $this->hasOne(PhoneChangeCode::class);
}
public function emailChangeCode()
{
return $this->hasOne(EmailChangeCode::class);
}
public function types()
{
return $this->belongsToMany(AccountType::class);
@ -259,26 +256,6 @@ class Account extends Authenticatable
return $externalAccount->save();
}
public function requestEmailUpdate(string $newEmail)
{
// Remove all the old requests
$this->emailChanged()->delete();
// Create a new one
$emailChanged = new EmailChanged;
$emailChanged->new_email = $newEmail;
$emailChanged->hash = Str::random(16);
$emailChanged->account_id = $this->id;
$emailChanged->save();
$this->refresh();
// Set it temporary to try to send the validation email
$this->email = $newEmail;
Mail::to($this)->send(new ChangingEmail($this));
}
public function generateApiKey(): ApiKey
{
$this->apiKey()->delete();
@ -339,7 +316,7 @@ class Account extends Authenticatable
->exists();
}
public function updatePassword($newPassword, ?string $algorithm = 'SHA-256')
public function updatePassword($newPassword, string $algorithm = 'SHA-256')
{
$this->passwords()->delete();

View file

@ -209,17 +209,6 @@ class ImportDatabase extends Command
->toArray();
DigestNonce::insert($originNonces);
$this->info('Migrating the email changed');
$originEmailChanged = Capsule::connection('sqlite')
->table('email_changed')
->get()
->map(function ($element) {
return (array)$element;
})
->toArray();
EmailChanged::insert($originEmailChanged);
$this->info('Migrating the phone change code');
$originPhoneChangeCodes = Capsule::connection('sqlite')

View file

@ -19,18 +19,30 @@
namespace App;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class EmailChanged extends Model
class EmailChangeCode extends Model
{
protected $table = 'email_changed';
protected $hidden = ['id', 'updated_at', 'hash', 'account_id'];
protected $casts = [
'created_at' => 'datetime:Y-m-d H:m:s',
];
use HasFactory;
protected $hidden = ['id', 'account_id', 'code'];
public function account()
{
return $this->belongsTo('App\Account');
}
public function validate(int $code): bool
{
return ($this->code == $code);
}
public function getObfuscatedEmailAttribute()
{
$stars = 4; // Min Stars to use
$at = strpos($this->attributes['email'], '@');
if ($at - 2 > $stars) $stars = $at - 2;
return substr($this->attributes['email'], 0, 1) . str_repeat('*', $stars) . substr($this->attributes['email'], $at - 1);
}
}

View file

@ -23,22 +23,12 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Account;
use App\AccountTombstone;
use App\Http\Requests\CreateAccountRequest;
use App\Services\AccountService;
class AccountController extends Controller
{
public function home(Request $request)
{
if ($request->user()) {
return redirect()->route('account.panel');
}
return view('account.home', [
'count' => Account::where('activated', true)->count()
]);
}
public function documentation(Request $request)
{
return view('account.documentation', [
@ -48,7 +38,7 @@ class AccountController extends Controller
public function panel(Request $request)
{
return view('account.panel', [
return view('account.dashboard', [
'account' => $request->user()
]);
}
@ -61,6 +51,23 @@ class AccountController extends Controller
return redirect()->back();
}
public function store(CreateAccountRequest $request)
{
$account = (new AccountService(api: false))->store($request);
Auth::login($account);
if ($request->has('phone')) {
(new AccountService(api: false))->requestPhoneChange($request);
return redirect()->route('account.phone.validate');
} elseif ($request->has('email')) {
(new AccountService(api: false))->requestEmailChange($request);
return redirect()->route('account.email.validate');
}
return abort(404);
}
public function delete(Request $request)
{
return view('account.delete', [

View file

@ -57,7 +57,7 @@ class AuthTokenController extends Controller
$request->session()->flash('success', 'Successfully authenticated');
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
/**
@ -74,6 +74,6 @@ class AuthTokenController extends Controller
$request->session()->flash('success', 'External device successfully authenticated');
}
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
}

View file

@ -23,13 +23,10 @@ use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
use App\Account;
use App\Alias;
use App\AuthToken;
use App\Libraries\OvhSMS;
use App\Mail\PasswordAuthentication;
class AuthenticateController extends Controller
{
@ -38,10 +35,12 @@ class AuthenticateController extends Controller
public function login(Request $request)
{
if (auth()->user()) {
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
return view('account.login');
return view('account.login', [
'count' => Account::where('activated', true)->count()
]);
}
public function authenticate(Request $request)
@ -74,184 +73,13 @@ class AuthenticateController extends Controller
bchash($account->username, $account->resolvedRealm, $request->get('password'), $password->algorithm)
)) {
Auth::login($account);
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
}
return redirect()->back()->withErrors(['authentication' => 'Wrong username or password']);
}
public function loginEmail(Request $request)
{
return view('account.login.email', [
'domain' => '@' . config('app.sip_domain')
]);
}
/**
* Display the form
*/
public function authenticateEmail(Request $request)
{
$rules = [
'email' => 'required|email|exists:accounts,email',
'g-recaptcha-response' => 'required|captcha',
];
if (config('app.account_email_unique') == false) {
$rules['username'] = 'required';
}
$request->validate($rules);
$account = Account::where('email', $request->get('email'));
/**
* Because several accounts can have the same email
*/
if (config('app.account_email_unique') == false) {
$account = $account->where('username', $request->get('username'));
}
$account = $account->first();
// Try alias
if (!$account) {
$alias = Alias::where('alias', $request->get('username'))->first();
if ($alias && $alias->account->email == $request->get('email')) {
$account = $alias->account;
}
}
if (!$account) {
return redirect()->back()->withErrors(['authentication' => 'The account doesn\'t exists']);
}
$account->confirmation_key = Str::random(self::$emailCodeSize);
$account->provision();
$account->save();
Mail::to($account)->send(new PasswordAuthentication($account));
return redirect()->route('account.check.email', $account->identifier);
}
/**
* A page that check if the email was validated and reload if not
*/
public function checkEmail(Request $request, string $sip)
{
if (auth()->user()) {
return redirect()->route('account.panel');
}
$account = Account::sip($sip)->firstOrFail();
return view('account.authenticate.email', [
'account' => $account
]);
}
public function validateEmail(Request $request, string $code)
{
$request->merge(['code' => $code]);
$request->validate(['code' => 'required|size:' . self::$emailCodeSize]);
$account = Account::where('confirmation_key', $code)->first();
if (!$account) {
return redirect()->route('account.login_email');
}
$account->confirmation_key = null;
// If there is already a password set, we directly activate the account
if ($account->passwords()->count() != 0) {
$account->activated = true;
}
$account->save();
Auth::login($account);
// Ask the user to set a password
if (!$account->activated) {
return redirect()->route('account.password');
}
return redirect()->route('account.panel');
}
public function loginPhone(Request $request)
{
return view('account.login.phone');
}
public function authenticatePhone(Request $request)
{
$request->validate([
'phone' => 'required|starts_with:+',
'g-recaptcha-response' => 'required|captcha',
]);
$account = Account::where('username', $request->get('phone'))
->first();
// Try alias
if (!$account) {
$alias = Alias::where('alias', $request->get('phone'))->first();
if ($alias) {
$account = $alias->account;
}
}
if (!$account) {
return redirect()->back()->withErrors([
'phone' => 'Invalid phone number'
]);
}
$account->confirmation_key = generatePin();
$account->save();
$ovhSMS = new OvhSMS;
$ovhSMS->send($request->get('phone'), 'Your ' . config('app.name') . ' validation code is ' . $account->confirmation_key);
// Ask the user to set a password
if (!$account->activated) {
return redirect()->route('account.password');
}
return view('account.authenticate.phone', [
'account' => $account
]);
}
public function validatePhone(Request $request)
{
$request->validate([
'account_id' => 'required',
'code' => 'required|digits:4'
]);
$account = Account::where('id', $request->get('account_id'))
->firstOrFail();
if ($account->confirmation_key != $request->get('code')) {
return redirect()->back()->withErrors([
'code' => 'Wrong code'
]);
}
$account->confirmation_key = null;
$account->save();
Auth::login($account);
return redirect()->route('account.panel');
}
public function loginAuthToken(Request $request, ?string $token = null)
{
$authToken = null;
@ -276,7 +104,7 @@ class AuthenticateController extends Controller
$request->session()->flash('success', 'Successfully authenticated');
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
return view('account.authenticate.auth_token', [

View file

@ -20,65 +20,40 @@
namespace App\Http\Controllers\Account;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use App\Mail\ChangedEmail;
use App\Services\AccountService;
class EmailController extends Controller
{
public function show(Request $request)
public function change(Request $request)
{
return view('account.email', [
return view('account.email.change', [
'account' => $request->user()
]);
}
public function requestUpdate(Request $request)
public function requestChange(Request $request)
{
$request->validate(
$request->user()->email
? [
'email_current' => ['required', Rule::in([$request->user()->email])],
'email' => config('app.account_email_unique')
? 'required|different:email_current|confirmed|email|unique:accounts,email'
: 'required|different:email_current|confirmed|email',
]
: [
'email' => config('app.account_email_unique')
? 'required|email|confirmed|unique:accounts,email'
: 'required|confirmed|email',
]
);
//$request->validate(['g-recaptcha-response' => 'required|captcha']);
$request->user()->requestEmailUpdate($request->get('email'));
(new AccountService(api: false))->requestEmailChange($request);
Log::channel('events')->info('Web: Email change requested', ['id' => $request->user()->identifier]);
$request->session()->flash('success', 'An email was sent with a confirmation link. Please click it to update your email address.');
return redirect()->route('account.panel');
return redirect()->route('account.email.validate');
}
public function update(Request $request, string $hash)
public function validateChange(Request $request)
{
$account = $request->user();
return view('account.email.validate', [
'emailChangeCode' => $request->user()->emailChangeCode()->firstOrFail()
]);
}
if ($account->emailChanged && $account->emailChanged->hash == $hash) {
$account->email = $account->emailChanged->new_email;
$account->save();
Mail::to($account)->send(new ChangedEmail());
$account->emailChanged->delete();
Log::channel('events')->info('Web: Email change updated', ['id' => $account->identifier]);
$request->session()->flash('success', 'Email successfully updated');
return redirect()->route('account.panel');
public function store(Request $request)
{
if ((new AccountService(api: false))->updateEmail($request)) {
return redirect()->route('account.dashboard');
}
abort(404);
return redirect()->route('account.email.change');
}
}

View file

@ -53,7 +53,7 @@ class PasswordController extends Controller
Log::channel('events')->info('Web: Password changed', ['id' => $account->identifier]);
$request->session()->flash('success', 'Password successfully changed');
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
Log::channel('events')->info('Web: Password set for the first time', ['id' => $account->identifier]);
@ -63,6 +63,6 @@ class PasswordController extends Controller
Mail::to($account)->send(new ConfirmedRegistration($account));
}
return redirect()->route('account.panel');
return redirect()->route('account.dashboard');
}
}

View file

@ -0,0 +1,59 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App\Http\Controllers\Account;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\AccountService;
class PhoneController extends Controller
{
public function change(Request $request)
{
return view('account.phone.change', [
'account' => $request->user()
]);
}
public function requestChange(Request $request)
{
//$request->validate(['g-recaptcha-response' => 'required|captcha']);
(new AccountService(api: false))->requestPhoneChange($request);
return redirect()->route('account.phone.validate');
}
public function validateChange(Request $request)
{
return view('account.phone.validate', [
'phoneChangeCode' => $request->user()->phoneChangeCode()->firstOrFail()
]);
}
public function store(Request $request)
{
if ((new AccountService(api: false))->updatePhone($request)) {
return redirect()->route('account.dashboard');
}
return redirect()->route('account.phone.change');
}
}

View file

@ -0,0 +1,114 @@
<?php
namespace App\Http\Controllers\Account;
use App\Account;
use App\Alias;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\AccountService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;
class RecoveryController extends Controller
{
public function showEmail(Request $request)
{
return view('account.recovery.show', [
'method' => 'email',
'domain' => resolveDomain($request)
]);
}
public function showPhone(Request $request)
{
return view('account.recovery.show', [
'method' => 'phone',
'domain' => resolveDomain($request)
]);
}
public function send(Request $request)
{
$rules = [
'email' => 'required_without:phone|email|exists:accounts,email',
'phone' => 'required_without:email|starts_with:+',
//'g-recaptcha-response' => 'required|captcha',
];
if ($request->get('email')) {
if (config('app.account_email_unique') == false) {
$rules['username'] = 'required';
}
$request->validate($rules);
$account = Account::where('email', $request->get('email'));
/**
* Because several accounts can have the same email
*/
if (config('app.account_email_unique') == false) {
$account = $account->where('username', $request->get('username'));
}
$account = $account->first();
// Try alias
if (!$account) {
$alias = Alias::where('alias', $request->get('username'))->first();
if ($alias && $alias->account->email == $request->get('email')) {
$account = $alias->account;
}
}
} elseif ($request->get('phone')) {
$account = Account::where('username', $request->get('phone'))->first();
// Try alias
if (!$account) {
$alias = Alias::where('alias', $request->get('phone'))->first();
if ($alias) {
$account = $alias->account;
}
}
}
if (!$account) {
return redirect()->back()->withErrors(['identifier' => 'The account doesn\'t exists']);
}
if ($request->get('email')) {
$account = (new AccountService)->recoverByEmail($account);
} elseif ($request->get('phone')) {
$account = (new AccountService)->recoverByPhone($account);
}
return view('account.recovery.confirm', [
'account_id' => Crypt::encryptString($account->id)
]);
}
public function confirm(Request $request)
{
$request->validate([
'account_id' => 'required',
'code' => 'required|digits:4'
]);
$account = Account::where('id', Crypt::decryptString($request->get('account_id')))->firstOrFail();
if ($account->recovery_code != $request->get('code')) {
return redirect()->back()->withErrors([
'code' => 'Wrong code'
]);
}
$account->recovery_code = null;
$account->save();
Auth::login($account);
return redirect()->route('account.dashboard');
}
}

View file

@ -21,159 +21,20 @@ namespace App\Http\Controllers\Account;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
use Carbon\Carbon;
use App\Account;
use App\Alias;
use App\Rules\WithoutSpaces;
use App\Rules\IsNotPhoneNumber;
use App\Rules\NoUppercase;
use App\Libraries\OvhSMS;
use App\Mail\RegisterConfirmation;
use App\Mail\NewsletterRegistration;
use App\Rules\BlacklistedUsername;
use App\Rules\SIPUsername;
class RegisterController extends Controller
{
private $emailCodeSize = 13;
public function register(Request $request)
{
if (config('app.phone_authentication') == false) {
return redirect()->route('account.register.email');
}
return view('account.register');
}
public function registerPhone(Request $request)
{
return view('account.register.phone', [
'domain' => '@' . config('app.sip_domain')
'domain' => resolveDomain($request)
]);
}
public function registerEmail(Request $request)
{
return view('account.register.email', [
'domain' => '@' . config('app.sip_domain')
]);
}
public function storeEmail(Request $request)
{
$request->validate([
'terms' => 'accepted',
'privacy' => 'accepted',
'username' => [
'required',
new NoUppercase,
Rule::unique('accounts', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),
Rule::unique('accounts_tombstones', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),
'filled',
new WithoutSpaces,
new IsNotPhoneNumber,
new BlacklistedUsername,
new SIPUsername
],
'g-recaptcha-response' => 'required|captcha',
'email' => config('app.account_email_unique')
? 'required|email|confirmed|unique:accounts,email'
: 'required|email|confirmed',
]);
$account = new Account;
$account->username = $request->get('username');
$account->email = $request->get('email');
$account->activated = false;
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
$account->creation_time = Carbon::now();
$account->user_agent = $request->header('User-Agent') ?? config('app.name');
$account->save();
$account->confirmation_key = Str::random($this->emailCodeSize);
$account->save();
if (!empty(config('app.newsletter_registration_address')) && $request->has('newsletter')) {
Mail::to(config('app.newsletter_registration_address'))->send(new NewsletterRegistration($account));
}
Log::channel('events')->info('Web: Account created using an email confirmation', ['id' => $account->identifier]);
Mail::to($account)->send(new RegisterConfirmation($account));
return redirect()->route('account.check.email', $account->identifier);
}
public function storePhone(Request $request)
{
$request->validate([
'terms' => 'accepted',
'privacy' => 'accepted',
'username' => [
new NoUppercase,
Rule::unique('accounts', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),
Rule::unique('accounts_tombstones', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),
'nullable',
new WithoutSpaces,
new IsNotPhoneNumber,
new BlacklistedUsername,
new SIPUsername
],
'phone' => [
'required', 'unique:aliases,alias',
'unique:accounts,username',
new WithoutSpaces, 'starts_with:+'
],
'email' => config('app.account_email_unique')
? 'nullable|email|unique:accounts,email'
: 'nullable|email',
'g-recaptcha-response' => 'required|captcha',
]);
$account = new Account;
$account->username = !empty($request->get('username'))
? $request->get('username')
: $request->get('phone');
$account->email = $request->get('email');
$account->activated = false;
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
$account->creation_time = Carbon::now();
$account->user_agent = $request->header('User-Agent') ?? config('app.name');
$account->save();
$alias = new Alias;
$alias->alias = $request->get('phone');
$alias->domain = config('app.sip_domain');
$alias->account_id = $account->id;
$alias->save();
$account->confirmation_key = generatePin();
$account->save();
$ovhSMS = new OvhSMS;
$ovhSMS->send($request->get('phone'), 'Your ' . config('app.name') . ' validation code is ' . $account->confirmation_key);
Log::channel('events')->info('Web: Account created using an SMS confirmation', ['id' => $account->identifier]);
return view('account.authenticate.phone', [
'account' => $account
'domain' => resolveDomain($request)
]);
}
}

View file

@ -32,6 +32,7 @@ use App\AccountTombstone;
use App\AccountCreationToken;
use App\Alias;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
use App\Http\Requests\CreateAccountRequest;
use App\Libraries\OvhSMS;
use App\Mail\RegisterConfirmation;
use App\Rules\AccountCreationToken as RulesAccountCreationToken;
@ -40,6 +41,7 @@ use App\Rules\IsNotPhoneNumber;
use App\Rules\NoUppercase;
use App\Rules\SIPUsername;
use App\Rules\WithoutSpaces;
use App\Services\AccountService;
class AccountController extends Controller
{
@ -232,57 +234,9 @@ class AccountController extends Controller
return $account;
}
public function store(Request $request)
public function store(CreateAccountRequest $request)
{
$request->validate([
'username' => [
'required',
new NoUppercase,
new IsNotPhoneNumber,
new BlacklistedUsername,
new SIPUsername,
Rule::unique('accounts', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),
Rule::unique('accounts_tombstones', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),
'filled',
],
'algorithm' => 'required|in:SHA-256,MD5',
'password' => 'required|filled',
'dtmf_protocol' => 'nullable|in:' . Account::dtmfProtocolsRule(),
'account_creation_token' => [
'required',
new RulesAccountCreationToken
],
'email' => config('app.account_email_unique')
? 'nullable|email|unique:accounts,email'
: 'nullable|email',
]);
$token = AccountCreationToken::where('token', $request->get('account_creation_token'))->first();
$token->used = true;
$token->save();
$account = new Account;
$account->username = $request->get('username');
$account->email = $request->get('email');
$account->activated = false;
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
$account->creation_time = Carbon::now();
$account->user_agent = config('app.name');
$account->dtmf_protocol = $request->get('dtmf_protocol');
$account->provision();
$account->save();
$account->updatePassword($request->get('password'), $request->get('algorithm'));
Log::channel('events')->info('API: Account created', ['id' => $account->identifier]);
// Full reload
return Account::withoutGlobalScopes()->find($account->id);
return (new AccountService)->store($request);
}
public function activateEmail(Request $request, string $sip)

View file

@ -20,22 +20,13 @@
namespace App\Http\Controllers\Api\Account;
use App\Http\Controllers\Controller;
use App\Services\AccountService;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class EmailController extends Controller
{
public function requestUpdate(Request $request)
{
$rules = ['required', 'email', Rule::notIn([$request->user()->email])];
if (config('app.account_email_unique')) {
array_push($rules, Rule::unique('accounts', 'email'));
}
$request->validate([
'email' => $rules,
]);
$request->user()->requestEmailUpdate($request->get('email'));
(new AccountService)->requestEmailChange($request);
}
}

View file

@ -20,69 +20,19 @@
namespace App\Http\Controllers\Api\Account;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use App\Rules\WithoutSpaces;
use App\Libraries\OvhSMS;
use App\PhoneChangeCode;
use App\Alias;
use App\Services\AccountService;
class PhoneController extends Controller
{
public function requestUpdate(Request $request)
{
$request->validate([
'phone' => [
'required', 'unique:aliases,alias',
'unique:accounts,username',
new WithoutSpaces, 'starts_with:+'
]
]);
$account = $request->user();
$phoneChangeCode = $account->phoneChangeCode ?? new PhoneChangeCode;
$phoneChangeCode->account_id = $account->id;
$phoneChangeCode->phone = $request->get('phone');
$phoneChangeCode->code = generatePin();
$phoneChangeCode->save();
Log::channel('events')->info('API: Account phone change requested by SMS', ['id' => $account->identifier]);
$ovhSMS = new OvhSMS;
$ovhSMS->send($request->get('phone'), 'Your ' . config('app.name') . ' validation code is ' . $phoneChangeCode->code);
return (new AccountService)->requestPhoneChange($request);
}
public function update(Request $request)
{
$request->validate([
'code' => 'required|digits:4'
]);
$account = $request->user();
$phoneChangeCode = $account->phoneChangeCode()->firstOrFail();
if ($phoneChangeCode->code == $request->get('code')) {
$account->alias()->delete();
$alias = new Alias;
$alias->alias = $phoneChangeCode->phone;
$alias->domain = config('app.sip_domain');
$alias->account_id = $account->id;
$alias->save();
Log::channel('events')->info('API: Account phone changed using SMS', ['id' => $account->identifier]);
$phoneChangeCode->delete();
$account->refresh();
return $account;
}
$phoneChangeCode->delete();
abort(403);
return (new AccountService)->updatePhone($request);
}
}

View file

@ -29,13 +29,10 @@ use App\Account;
use App\AccountTombstone;
use App\AccountType;
use App\ActivationExpiration;
use App\Admin;
use App\Alias;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
use App\Http\Requests\CreateAccountRequest;
use App\Http\Requests\UpdateAccountRequest;
use App\Mail\PasswordAuthentication;
use Illuminate\Support\Facades\Mail;
use App\Services\AccountService;
class AccountController extends Controller
{
@ -54,7 +51,7 @@ class AccountController extends Controller
return Account::sip($sip)->firstOrFail();
}
public function searchByEmail(Request $request, string $email)
public function searchByEmail(string $email)
{
return Account::where('email', $email)->firstOrFail();
}
@ -211,13 +208,7 @@ class AccountController extends Controller
public function recoverByEmail(int $id)
{
$account = Account::findOrFail($id);
$account->provision();
$account->confirmation_key = Str::random(WebAuthenticateController::$emailCodeSize);
$account->save();
Log::channel('events')->info('API Admin: Sending recovery email', ['id' => $account->identifier]);
Mail::to($account)->send(new PasswordAuthentication($account));
$account = (new AccountService)->recoverByEmail($account);
return $account->makeVisible(['confirmation_key', 'provisioning_token']);
}

View file

@ -31,6 +31,9 @@ class CreateAccountRequest extends FormRequest
Rule::unique('accounts', 'username')->where(function ($query) {
$query->where('domain', resolveDomain($this));
}),
/*Rule::unique('accounts_tombstones', 'username')->where(function ($query) use ($request) {
$query->where('domain', config('app.sip_domain'));
}),*/
'filled',
],
'password' => 'required|min:3',

View file

@ -1,35 +0,0 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ChangedEmail extends Mailable
{
use Queueable, SerializesModels;
public function build()
{
return $this->view('mails.changed_email')
->text('mails.changed_email_text');
}
}

View file

@ -25,7 +25,7 @@ use Illuminate\Queue\SerializesModels;
use App\Account;
class PasswordAuthentication extends Mailable
class RecoverByCode extends Mailable
{
use Queueable, SerializesModels;
@ -45,7 +45,7 @@ class PasswordAuthentication extends Mailable
? 'mails.authentication_text_custom'
: 'mails.authentication_text')
->with([
'link' => route('account.authenticate.email_confirm', [$this->account->confirmation_key]),
'recovery_code' => $this->account->recovery_code,
'provisioning_link' => route('provisioning.show', [
'provisioning_token' => $this->account->provisioning_token,
'reset_password' => true

View file

@ -25,7 +25,7 @@ use Illuminate\Queue\SerializesModels;
use App\Account;
class ChangingEmail extends Mailable
class RegisterValidation extends Mailable
{
use Queueable, SerializesModels;
@ -38,8 +38,10 @@ class ChangingEmail extends Mailable
public function build()
{
return $this->view('mails.changing_email')
->text('mails.changing_email_text')
->with(['account' => $this->account]);
return $this->view('mails.register_validate')
->text('mails.register_validate_text')
->with([
'code' => $this->account->emailChangeCode()->first()->code
]);
}
}

View file

@ -26,6 +26,8 @@ class PhoneChangeCode extends Model
{
use HasFactory;
protected $hidden = ['id', 'account_id', 'code'];
public function account()
{
return $this->belongsTo('App\Account');

View file

@ -0,0 +1,259 @@
<?php
/*
Flexisip Account Manager is a set of tools to manage SIP accounts.
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace App\Services;
use App\Account;
use App\AccountCreationToken;
use App\Alias;
use App\EmailChangeCode;
use App\Http\Requests\CreateAccountRequest;
use App\Libraries\OvhSMS;
use App\Mail\NewsletterRegistration;
use App\Mail\RecoverByCode;
use App\Mail\RegisterValidation;
use App\PhoneChangeCode;
use Illuminate\Support\Facades\Log;
use App\Rules\AccountCreationToken as RulesAccountCreationToken;
use App\Rules\WithoutSpaces;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class AccountService
{
public function __construct(public bool $api = true)
{
}
/**
* Account creation
*/
public function store(CreateAccountRequest $request): Account
{
$rules = [];
$rules['password'] = 'confirmed';
$rules['email'] = 'confirmed';
$rules['privacy'] = 'accepted';
$rules['terms'] = 'accepted';
if ($this->api) {
$rules = [];
$rules['account_creation_token'] = ['required', new RulesAccountCreationToken];
}
$request->validate($rules);
if ($this->api) {
$token = AccountCreationToken::where('token', $request->get('account_creation_token'))->first();
$token->used = true;
$token->save();
}
$account = new Account;
$account->username = $request->get('username');
$account->activated = false;
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
$account->creation_time = Carbon::now();
$account->user_agent = config('app.name');
$account->dtmf_protocol = $request->get('dtmf_protocol');
$account->confirmation_key = generatePin();
$account->provision();
$account->save();
$account->updatePassword($request->get('password'), $request->has('algorithm') ? $request->get('algorithm') : 'SHA-256');
Log::channel('events')->info('Account Service: Account created', ['id' => $account->identifier]);
if (!$this->api) {
if (!empty(config('app.newsletter_registration_address')) && $request->has('newsletter')) {
Mail::to(config('app.newsletter_registration_address'))->send(new NewsletterRegistration($account));
}
}
return Account::withoutGlobalScopes()->find($account->id);
}
/**
* Link a phone number to an account
*/
public function requestPhoneChange(Request $request)
{
$request->validate([
'phone' => [
'required', 'unique:aliases,alias',
'unique:accounts,username',
new WithoutSpaces, 'starts_with:+'
]
]);
$account = $request->user();
$phoneChangeCode = $account->phoneChangeCode ?? new PhoneChangeCode();
$phoneChangeCode->account_id = $account->id;
$phoneChangeCode->phone = $request->get('phone');
$phoneChangeCode->code = generatePin();
$phoneChangeCode->save();
Log::channel('events')->info('Account Service: Account phone change requested by SMS', ['id' => $account->identifier]);
$ovhSMS = new OvhSMS;
$ovhSMS->send($request->get('phone'), 'Your ' . config('app.name') . ' validation code is ' . $phoneChangeCode->code);
}
public function updatePhone(Request $request): ?Account
{
$request->validate([
'code' => 'required|digits:4'
]);
$account = $request->user();
$phoneChangeCode = $account->phoneChangeCode()->firstOrFail();
if ($phoneChangeCode->code == $request->get('code')) {
$account->alias()->delete();
$alias = new Alias;
$alias->alias = $phoneChangeCode->phone;
$alias->domain = config('app.sip_domain');
$alias->account_id = $account->id;
$alias->save();
Log::channel('events')->info('Account Service: Account phone changed using SMS', ['id' => $account->identifier]);
$phoneChangeCode->delete();
$account->activated = true;
$account->save();
$account->refresh();
return $account;
}
$phoneChangeCode->delete();
if ($this->api) {
abort(403);
}
return null;
}
/**
* Link an email to an account
*/
public function requestEmailChange(Request $request)
{
$rules = ['required', 'email', Rule::notIn([$request->user()->email])];
if (config('app.account_email_unique')) {
array_push($rules, Rule::unique('accounts', 'email'));
}
$request->validate([
'email' => $rules,
]);
$account = $request->user();
$emailChangeCode = $account->emailChangeCode ?? new EmailChangeCode;
$emailChangeCode->account_id = $account->id;
$emailChangeCode->email = $request->get('email');
$emailChangeCode->code = generatePin();
$emailChangeCode->save();
Log::channel('events')->info('Account Service: Account email change requested by email', ['id' => $account->identifier]);
Mail::to($emailChangeCode->email)->send(new RegisterValidation($account));
}
public function updateEmail(Request $request): ?Account
{
$request->validate([
'code' => 'required|digits:4'
]);
$account = $request->user();
$emailChangeCode = $account->emailChangeCode()->firstOrFail();
if ($emailChangeCode->validate($request->get('code'))) {
$account->email = $emailChangeCode->email;
$account->save();
Log::channel('events')->info('Account Service: Account email changed using email', ['id' => $account->identifier]);
$emailChangeCode->delete();
$account->activated = true;
$account->save();
$account->refresh();
return $account;
}
$emailChangeCode->delete();
if ($this->api) {
abort(403);
}
return null;
}
/**
* Account recovery
*/
public function recoverByEmail(Account $account): Account
{
$account = $this->recoverAccount($account);
Mail::to($account)->send(new RecoverByCode($account));
Log::channel('events')->info('Account Service: Sending recovery email', ['id' => $account->identifier]);
return $account;
}
public function recoverByPhone(Account $account): Account
{
$account = $this->recoverAccount($account);
$ovhSMS = new OvhSMS;
$ovhSMS->send($account->phone, 'Your ' . config('app.name') . ' validation code is ' . $account->recovery_code);
Log::channel('events')->info('Account Service: Sending recovery SMS', ['id' => $account->identifier]);
return $account;
}
private function recoverAccount(Account $account): Account
{
$account->recovery_code = generatePin();
$account->provision();
$account->save();
return $account;
}
}

View file

@ -8,15 +8,15 @@
],
"license": "MIT",
"require": {
"php": ">=8.0",
"php": ">=8.0.2",
"anhskohbo/no-captcha": "^3.3",
"doctrine/dbal": "^3.6",
"endroid/qr-code": "^4.1",
"fakerphp/faker": "^1.21",
"laravel/framework": "^9.0",
"laravel/framework": "^9.5",
"laravelcollective/html": "^6.4",
"namoshek/laravel-redis-sentinel": "^0.1.2",
"ovh/ovh": "^2.0",
"ovh/ovh": "^3.0",
"parsedown/laravel": "^1.2",
"react/socket": "^1.10",
"respect/validation": "^2.2"

1282
flexiapi/composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -80,7 +80,7 @@ return [
|
*/
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'encryption' => env('MAIL_ENCRYPTION', ''),
/*
|--------------------------------------------------------------------------

View file

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddEmailChangeCodeTable extends Migration
{
public function up()
{
Schema::create('email_change_codes', function (Blueprint $table) {
$table->increments('id');
$table->integer('account_id')->unsigned();
$table->string('code');
$table->string('email');
$table->timestamps();
$table->foreign('account_id')->references('id')
->on('accounts')->onDelete('cascade');
});
Schema::dropIfExists('email_changed');
}
public function down()
{
Schema::dropIfExists('email_change_codes');
Schema::create('email_changed', function (Blueprint $table) {
$table->increments('id');
$table->integer('account_id')->unsigned()->unique();
$table->string('new_email');
$table->string('hash');
$table->timestamps();
$table->foreign('account_id')->references('id')
->on('accounts')->onDelete('cascade');
});
}
}

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('accounts', function (Blueprint $table) {
$table->integer('recovery_code')->unsigned()->nullable();
});
}
public function down()
{
Schema::table('accounts', function (Blueprint $table) {
$table->dropColumn('recovery_code');
});
}
};

File diff suppressed because one or more lines are too long

574
flexiapi/public/css/far.css vendored Normal file
View file

@ -0,0 +1,574 @@
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;200;300;400;500;600;700;800;900&family=Roboto:wght@300;400;500;700&display=swap');
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/materialicons/v140/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-font-feature-settings: 'liga';
-moz-osx-font-smoothing: grayscale;
}
* {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
list-style-type: none;
text-decoration: none;
box-shadow: none;
outline: none;
}
html {
font-size: 10px;
}
body {
font-family: 'Noto Sans', sans-serif;
font-weight: 400;
display: flex;
flex-direction: column;
overflow-x: hidden;
--main-1: rgba(255, 234, 203, 1);
--main-2: rgba(255, 208, 152, 1);
--main-3: rgba(255, 178, 102, 1);
--main-4: rgba(255, 146, 63, 1);
--main-5: rgba(254, 94, 0, 1);
--main-6: rgba(218, 68, 0, 1);
--main-7: rgba(183, 45, 0, 1);
--main-8: rgba(147, 28, 0, 1);
--main-9: rgba(121, 15, 0, 1);
--second-0: rgba(250, 254, 255, 1);
--second-1: rgba(238, 246, 248, 1);
--second-2: rgba(223, 236, 242, 1);
--second-3: rgba(192, 209, 217, 1);
--second-4: rgba(154, 171, 181, 1);
--second-5: rgba(108, 122, 135, 1);
--second-6: rgba(78, 96, 116, 1);
--second-7: rgba(54, 72, 96, 1);
--second-8: rgba(34, 51, 77, 1);
--second-9: rgba(20, 34, 64, 1);
--second-1: rgba(0, 0, 0, 1);
--grey-1: rgba(249, 249, 249, 1);
--grey-2: rgba(237, 237, 237, 1);
--grey-3: rgba(201, 201, 201, 1);
--grey-4: rgba(148, 148, 148, 1);
--grey-5: rgba(78, 78, 78, 1);
--grey-6: rgba(67, 57, 57, 1);
--grey-7: rgba(56, 39, 42, 1);
--grey-8: rgba(45, 24, 30, 1);
--grey-9: rgba(0, 0, 0, 1);
--danger-1: rgba(250, 232, 222, 1);
--danger-2: rgba(245, 204, 190, 1);
--danger-3: rgba(238, 170, 155, 1);
--danger-4: rgba(230, 137, 130, 1);
--danger-5: rgba(221, 95, 95, 1);
--danger-6: rgba(189, 72, 82, 1);
--danger-7: rgba(158, 53, 72, 1);
--danger-8: rgba(127, 37, 61, 1);
--danger-9: rgba(104, 26, 54, 1);
}
p,
a {
font-size: 1.5rem;
color: var(--second-7);
}
p {
margin-bottom: 1rem;
}
p b {
font-weight: bold;
}
p>a:not(.btn),
label>a {
text-decoration: underline;
color: var(--main-5);
}
content {
display: flex;
box-sizing: border-box;
gap: 3rem;
min-height: calc(100vh - 11rem);
}
body.welcome content {
min-height: calc(100vh - 20rem);
align-items: center;
margin: 0 auto;
max-width: 1024px;
}
/** Tabs **/
ul.tabs {
display: flex;
flex-direction: row;
border-bottom: 2px solid var(--grey-2);
margin-bottom: 2rem;
width: 100%;
}
ul.tabs li {
font-weight: 800;
color: var(--main-6);
border-bottom: 2px solid transparent;
line-height: 4rem;
font-size: 3rem;
margin: 0 1rem;
margin-bottom: -2px;
opacity: 0.5;
}
ul.tabs li a {
display: block;
}
ul.tabs li.current,
ul.tabs li:hover {
opacity: 1;
}
ul.tabs li:first-child {
margin-left: 0;
}
ul.tabs li:last-child {
margin-right: 0;
}
ul.tabs li.current {
border-color: var(--main-5);
}
/** Header **/
header nav {
padding: 3rem 1rem;
display: flex;
}
header nav a {
display: flex;
align-items: center;
padding: 0 4rem;
line-height: 4rem;
color: var(--second-5);
}
header nav a i {
margin-right: 1rem;
font-size: 2.5rem;
}
header nav a:first-child {
font-weight: 700;
padding-left: 2rem;
}
header nav a:first-child::before {
content: '';
width: 3rem;
height: 3rem;
padding: 1rem;
background-image: url('/img/logo.svg');
background-color: var(--main-5);
background-size: 3rem;
background-position: center;
background-repeat: no-repeat;
display: block;
margin-right: 1.5rem;
border-radius: 1rem;
box-shadow: 0 0 2rem rgba(0, 0, 0, 0.2);
}
header nav a:nth-child(2) {
margin-left: auto;
}
header nav a:last-child {
padding-right: 2rem;
}
/** Section **/
content section {
margin: 0 auto;
max-width: 1024px;
padding: 1rem;
box-sizing: border-box;
}
/** Sidenav **/
content nav {
background-color: var(--main-5);
width: 20rem;
margin-left: 0;
padding: 2rem;
border-radius: 0 3rem 0 0;
padding-bottom: 10rem;
padding-top: 4rem;
background-size: auto 10rem;
background-position: bottom center;
background-repeat: repeat-x;
background-image: url('/img/footer.svg');
}
content nav a {
color: white;
font-weight: 600;
font-size: 1.75rem;
display: flex;
align-items: center;
line-height: 5rem;
margin: 1rem 0;
margin-left: 2rem;
padding-right: 2rem;
position: relative;
}
content nav a.current {
background-color: white;
border-radius: 4rem;
color: var(--main-5);
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.2);
}
content nav a.current:before {
content: '';
display: block;
width: 1rem;
height: 1rem;
background-color: white;
border-radius: 1rem;
position: absolute;
left: -2rem;
top: 50% - 0.5rem;
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.2);
}
content nav a i {
margin: 0 1rem;
margin-left: 2rem;
font-size: 2rem;
}
/** Footer **/
body.welcome::after {
background-position: bottom center;
background-repeat: repeat-x;
background-image: url('/img/footer.svg');
display: block;
height: 10rem;
width: 100%;
background-size: 50rem;
content: '';
height: 9rem;
}
/** Titles **/
h1 {
font-size: 3.5rem;
line-height: 4rem;
font-weight: 800;
color: var(--second-6);
margin-bottom: 1rem;
display: flex;
align-items: center;
margin-right: 1rem;
}
h1 i {
font-size: 3rem;
margin-right: 1rem;
}
/** Forms **/
.btn {
display: inline-block;
background-color: var(--main-5);
font-weight: 600;
border: 1px solid var(--main-5);
border-radius: 3rem;
font-size: 1.5rem;
line-height: 2rem;
padding: 1rem 2rem;
color: white;
margin: 0 1rem;
}
.btn i {
margin-right: 0.5rem;
margin-left: -0.5rem;
font-size: 2rem;
vertical-align: middle;
}
.oppose {
float: right;
}
.btn[disabled] {
color: var(--main-5);
border-color: var(--main-5);
background-color: var(--main-1);
opacity: 0.5;
pointer-events: none;
}
.btn:hover {
background-color: var(--main-6);
border-color: var(--main-6);
cursor: pointer;
}
.btn:active {
background-color: var(--main-7);
border-color: var(--main-7);
}
.btn.btn-secondary {
background-color: transparent;
color: var(--main-5);
}
.btn.btn-secondary:hover {
background-color: var(--main-1);
}
.btn.btn-secondary:active {
background-color: var(--main-5);
border-color: var(--main-5);
color: white;
}
form {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem 0.5rem;
}
form div {
position: relative;
min-height: 4rem;
}
form .large {
grid-column: 1/-1;
}
@media screen and (max-width: 1024px) {
form div {
grid-column: 1/-1;
}
}
form small {
display: block;
font-weight: 300;
color: var(--second-6);
font-size: 1.25rem;
margin-top: 0.25rem;
}
form label {
color: var(--second-6);
font-size: 1.5rem;
}
form input[required]+label:after {
content: '*';
margin-left: 0.5rem;
}
form input:not([type=checkbox])+label,
form select+label {
position: absolute;
top: 0;
left: 0;
font-weight: 700;
}
form div .btn {
position: absolute;
bottom: 0;
}
form div .btn.oppose {
right: 0;
}
form div input,
form div select {
padding: 1rem 2rem;
background-color: var(--grey-1);
border-radius: 3rem;
border: 1px solid var(--grey-2);
font-size: 1.5rem;
}
form div select {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
}
form div.checkbox {
min-height: 2rem;
}
form div.select:after {
font-family: 'Material Icons';
content: "\e5cf";
display: block;
font-size: 3rem;
color: var(--second-6);
position: absolute;
right: 1rem;
bottom: 0rem;
pointer-events: none;
line-height: 4rem;
}
form div input[disabled] {
border-color: var(--grey-4);
color: var(--grey-4);
background-color: var(--grey-2);
pointer-events: none;
}
form div input[type=checkbox] {
margin-right: 1rem;
}
form div input:not([type=checkbox]):not(.btn),
form div select {
margin-top: 2.5rem;
box-sizing: border-box;
min-width: 65%;
}
form div input:autofill {
background: var(--grey-1);
}
form div input:hover,
form div select:hover {
border-color: var(--second-4);
}
form div input:focus-visible, form div input:active {
color: var(--main-5);
border-color: var(--main-5);
}
form div input:focus-visible+label, form div input:active+label {
color: var(--main-5);
}
form div input:invalid {
border-color: var(--danger-6);
color: var(--danger-5);
}
form div input:invalid+label {
color: var(--danger-5);
}
/** Badge **/
.badge {
background-color: var(--main-5);
color: white;
border-radius: 0.5rem;
font-weight: 700;
line-height: 2rem;
font-size: 1.25rem;
padding: 0.25rem 1.25rem;
}
/** Table **/
table {
border-collapse: collapse;
width: 100%;
}
table tr th {
text-transform: uppercase;
font-weight: 600;
color: var(--second-4);
text-align: left;
}
table thead {
border-bottom: 1px solid var(--second-4);
}
table tr td,
table tr th {
line-height: 4rem;
padding: 0 2rem;
font-size: 1.5rem;
}
table tr:nth-child(2n) {
background-color: var(--grey-1);
}
/* Display/hide */
.on_mobile {
display: none !important;
}
.on_mobile_after:after {
display: none !important;
}
@media screen and (max-width: 1024px) {
.on_mobile,
.on_mobile_after:after {
display: inherit !important;
}
.on_mobile.material-icons {
display: inline !important;
}
.on_desktop {
display: none !important;
}
}

View file

@ -0,0 +1,47 @@
<svg width="477" height="346" viewBox="0 0 477 346" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.8">
<path d="M64.2448 41.4346C64.1387 40.9039 64.0325 40.2669 64.0325 39.7362C64.0325 38.9931 63.9264 38.3562 63.8202 37.6132C63.7141 36.6578 63.5018 35.7024 63.2895 34.8532C62.6526 32.3056 61.6972 29.8641 60.4234 27.6349C58.725 24.6627 56.3896 22.0089 53.7358 19.992C50.4452 17.4444 46.7299 15.6398 42.59 14.7906C37.9193 13.8353 33.1425 14.1537 28.578 15.6398C26.1365 16.489 23.9073 17.6567 21.7843 19.1428C20.7228 19.8859 19.6613 20.7351 18.7059 21.5843C17.7505 22.5397 16.9013 23.495 16.0521 24.5565C8.93998 33.6856 8.62153 46.4237 15.3091 55.8712C16.0521 57.0389 16.9013 58.1004 17.8567 59.0557C18.8121 60.0111 19.7674 60.8603 20.8289 61.7095C22.8458 63.3018 25.075 64.5756 27.4103 65.531C39.2993 70.2016 52.7805 65.7433 59.6803 55.022C61.0603 52.899 62.228 50.4575 62.971 48.016C63.2895 47.0606 63.5018 46.2114 63.6079 45.2561C63.7141 44.6192 63.9264 43.8761 63.9264 43.2392C64.0325 42.6023 64.1387 41.9654 64.2448 41.4346C64.351 41.9654 64.351 42.6023 64.2448 43.133C64.2448 43.8761 64.1387 44.513 64.0325 45.2561C63.9264 46.2114 63.7141 47.1668 63.5018 48.1221C62.8649 50.6698 61.8034 53.2174 60.4234 55.4466C56.2835 62.2403 49.3836 66.9109 41.5284 68.1847C36.6455 69.0339 31.5502 68.5032 26.8796 66.6986C24.3319 65.7433 21.9966 64.3633 19.8736 62.771C18.8121 61.9218 17.7505 60.9665 16.7952 60.0111C15.8398 58.9496 14.9906 57.8881 14.1414 56.7204C7.13541 46.7422 7.45386 33.3671 14.9906 23.7073C15.8398 22.5397 16.7952 21.4782 17.8567 20.5228C18.9182 19.5674 19.9797 18.7182 21.1474 17.869C23.3766 16.2767 25.7119 15.1091 28.3657 14.2599C37.3885 11.2876 47.2606 13.0922 54.5851 19.0367C57.345 21.2659 59.5742 24.0258 61.3787 27.1042C62.6526 29.4395 63.6079 31.881 64.1387 34.5348C64.351 35.4901 64.4571 36.4455 64.5633 37.4009C64.6694 38.1439 64.6694 38.7808 64.6694 39.5239C64.351 40.2669 64.351 40.7977 64.2448 41.4346Z" fill="#EBEBEB"/>
<path d="M58.8311 24.4501C58.8311 24.5562 58.4065 24.9808 57.4512 25.6177L55.7527 26.8916C55.4343 27.1039 55.0097 27.4223 54.6912 27.6346L53.3113 28.3777C48.4283 31.1376 42.9084 32.5176 37.2824 32.4114L34.8409 32.3053L32.5056 31.9868C32.081 31.8807 31.7625 31.8807 31.3379 31.7745L30.2764 31.4561C29.5334 31.2438 28.8965 31.1376 28.1534 30.8192C25.8181 30.0761 23.4827 29.0146 21.3597 27.7408C17.7506 25.5116 15.946 23.4947 16.0521 23.3886C16.1583 23.2824 18.1752 24.8747 21.8905 26.7854C24.0135 27.8469 26.3488 28.8023 28.578 29.5453C29.2149 29.7576 29.8518 29.8638 30.5949 30.0761L31.6564 30.2884L32.7179 30.5007L34.9471 30.8192L37.2824 30.9253C42.6961 31.0315 48.0037 29.8638 52.7805 27.4223L54.1605 26.8916C54.5851 26.6793 55.0097 26.4669 55.3281 26.2546L57.2389 25.1931C58.1942 24.6624 58.725 24.3439 58.8311 24.4501Z" fill="#EBEBEB"/>
<path d="M15.3091 57.5696C15.2029 57.4634 15.7337 57.0388 16.6891 56.4019L18.4936 55.1281C18.8121 54.9158 19.2367 54.5973 19.5552 54.385L20.9351 53.642C25.8181 50.882 31.338 49.5021 36.964 49.6082L39.4055 49.7144L41.7408 50.139C42.1654 50.2451 42.4838 50.2451 42.9084 50.3513L43.97 50.6697C44.713 50.882 45.3499 50.9882 46.093 51.2005C48.4283 52.0497 50.7636 53.1112 52.8867 54.385C56.4958 56.6142 58.3004 58.6311 58.1942 58.7373C58.0881 58.8434 56.0712 57.2511 52.3559 55.3404C50.2329 54.2789 48.0037 53.3235 45.6684 52.5805C45.0315 52.3682 44.2884 52.262 43.6515 52.0497L42.59 51.8374L41.5285 51.6251L39.2993 51.3067L36.964 51.2005C31.5503 51.0943 26.2427 52.262 21.4659 54.7035L20.0859 55.3404C19.6613 55.5527 19.3429 55.765 18.9182 55.9773L17.0075 57.0388C15.946 57.3573 15.3091 57.5696 15.3091 57.5696Z" fill="#EBEBEB"/>
<path d="M64.2448 42.0716C64.2448 42.4962 52.0374 42.4962 37.0701 42.1777C22.1028 41.8593 9.89536 41.2224 9.89536 40.7977C9.89536 40.3731 22.1028 40.3731 37.0701 40.6916C52.0374 41.0101 64.2448 41.647 64.2448 42.0716Z" fill="#EBEBEB"/>
<path d="M35.7963 67.9722C35.3717 67.9722 35.3717 55.7648 35.6902 40.6913C36.0086 25.6179 36.6455 13.5166 37.0701 13.5166C37.4947 13.6228 37.4947 25.724 37.1763 40.7975C36.8578 55.871 36.2209 67.9722 35.7963 67.9722Z" fill="#EBEBEB"/>
<path d="M35.7963 67.9723C35.6902 68.0784 32.7179 66.0615 29.2149 61.497C27.1981 58.7371 25.6058 55.7649 24.332 52.6865C22.8459 48.7589 22.1028 44.619 22.209 40.4791C22.3151 36.3392 23.2705 32.1993 24.9689 28.3778C26.3489 25.2994 28.1534 22.4333 30.2765 19.7796C33.9918 15.3212 36.964 13.4105 37.0701 13.5166C37.1763 13.6228 34.4164 15.9581 31.1257 20.4165C29.2149 23.0703 27.5165 25.9363 26.2427 28.9086C24.6504 32.5177 23.8012 36.4453 23.6951 40.4791C23.5889 44.4067 24.2258 48.4404 25.6058 52.1557C26.6673 55.2341 28.1534 58.1002 30.0642 60.8601C33.2487 65.5308 35.9025 67.76 35.7963 67.9723Z" fill="#EBEBEB"/>
<path d="M37.707 13.6229C37.8132 13.5167 40.7854 15.5336 44.2884 20.0981C46.3053 22.7519 47.8976 25.7241 49.1714 28.9087C50.6575 32.8363 51.4006 36.9762 51.2944 41.1161C51.1883 45.256 50.2329 49.3959 48.5345 53.2173C47.1545 56.2957 45.3499 59.1618 43.2269 61.8156C39.5116 66.2739 36.5394 68.1847 36.4332 68.0785C36.3271 67.9723 38.9809 65.637 42.3777 61.1787C44.3946 58.5249 45.9868 55.6588 47.2607 52.6866C48.8529 49.0774 49.7021 45.1498 49.8083 41.1161C49.9144 37.1885 49.2775 33.1547 47.8976 29.4394C46.8361 26.361 45.2438 23.4949 43.4392 20.735C40.2547 16.0643 37.6009 13.729 37.707 13.6229Z" fill="#EBEBEB"/>
<path d="M476.537 249.173C476.43 248.642 476.324 248.005 476.324 247.474C476.324 246.731 476.218 246.094 476.112 245.351C476.006 244.396 475.794 243.44 475.581 242.591C474.944 240.044 473.989 237.602 472.715 235.373C471.017 232.401 468.681 229.747 466.028 227.73C462.737 225.182 459.022 223.378 454.882 222.529C450.211 221.573 445.434 221.892 440.87 223.378C438.428 224.227 436.199 225.395 434.076 226.881C433.015 227.624 431.953 228.473 430.998 229.322C430.042 230.278 429.193 231.233 428.344 232.295C421.232 241.424 420.913 254.162 427.601 263.609C428.344 264.777 429.193 265.838 430.148 266.794C431.104 267.749 432.059 268.598 433.121 269.448C435.138 271.04 437.367 272.314 439.702 273.269C451.591 277.94 465.072 273.481 471.972 262.76C473.352 260.637 474.52 258.195 475.263 255.754C475.581 254.799 475.794 253.949 475.9 252.994C476.006 252.357 476.218 251.614 476.218 250.977C476.324 250.34 476.43 249.703 476.537 249.173C476.643 249.703 476.643 250.34 476.537 250.871C476.537 251.614 476.43 252.251 476.324 252.994C476.218 253.949 476.006 254.905 475.794 255.86C475.157 258.408 474.095 260.955 472.715 263.185C468.575 269.978 461.675 274.649 453.82 275.923C448.937 276.772 443.842 276.241 439.171 274.437C436.624 273.481 434.288 272.101 432.165 270.509C431.104 269.66 430.042 268.704 429.087 267.749C428.132 266.688 427.282 265.626 426.433 264.458C419.427 254.48 419.746 241.105 427.282 231.445C428.132 230.278 429.087 229.216 430.148 228.261C431.21 227.305 432.271 226.456 433.439 225.607C435.668 224.015 438.004 222.847 440.657 221.998C449.68 219.026 459.552 220.83 466.877 226.775C469.637 229.004 471.866 231.764 473.67 234.842C474.944 237.178 475.9 239.619 476.43 242.273C476.643 243.228 476.749 244.184 476.855 245.139C476.961 245.882 476.961 246.519 476.961 247.262C476.749 248.005 476.643 248.536 476.537 249.173Z" fill="#EBEBEB"/>
<path d="M471.123 232.189C471.123 232.295 470.698 232.719 469.743 233.356L468.044 234.63C467.726 234.842 467.301 235.161 466.983 235.373L465.603 236.116C460.72 238.876 455.2 240.256 449.574 240.15L447.133 240.044L444.797 239.619C444.373 239.513 444.054 239.513 443.63 239.407L442.568 239.088C441.825 238.876 441.188 238.77 440.445 238.558C438.11 237.815 435.774 236.753 433.651 235.479C430.042 233.25 428.238 231.233 428.344 231.127C428.45 231.021 430.467 232.613 434.182 234.524C436.305 235.585 438.641 236.541 440.87 237.284C441.507 237.496 442.144 237.602 442.887 237.815L443.948 238.027L445.01 238.133L447.239 238.452L449.574 238.558C454.988 238.664 460.295 237.496 465.072 235.055L466.452 234.524C466.877 234.312 467.301 234.099 467.62 233.887L469.531 232.825C470.486 232.401 471.017 232.082 471.123 232.189Z" fill="#EBEBEB"/>
<path d="M427.601 265.308C427.495 265.201 428.025 264.777 428.981 264.14L430.785 262.866C431.104 262.654 431.528 262.335 431.847 262.123L433.227 261.38C438.11 258.62 443.63 257.24 449.256 257.346L451.697 257.452L454.033 257.877C454.457 257.983 454.776 257.983 455.2 258.089L456.262 258.408C457.005 258.62 457.642 258.726 458.385 258.939C460.72 259.788 463.055 260.849 465.178 262.123C468.788 264.352 470.592 266.369 470.486 266.475C470.38 266.581 468.363 264.989 464.648 263.078C462.525 262.017 460.295 261.062 457.96 260.319C457.323 260.106 456.58 260 455.943 259.788L454.882 259.575L453.82 259.469L451.591 259.151L449.256 259.045C443.842 258.939 438.534 260.106 433.758 262.548L432.378 263.078C431.953 263.291 431.635 263.503 431.21 263.715L429.299 264.777C428.238 265.095 427.601 265.414 427.601 265.308Z" fill="#EBEBEB"/>
<path d="M476.537 249.81C476.537 250.234 464.329 250.234 449.362 249.916C434.395 249.491 422.187 248.96 422.187 248.536C422.187 248.111 434.395 248.111 449.362 248.43C464.435 248.748 476.537 249.385 476.537 249.81Z" fill="#EBEBEB"/>
<path d="M448.088 275.711C447.663 275.711 447.663 263.503 447.982 248.536C448.3 233.568 448.937 221.361 449.362 221.361C449.786 221.361 449.786 233.568 449.468 248.536C449.15 263.503 448.513 275.711 448.088 275.711Z" fill="#EBEBEB"/>
<path d="M448.088 275.71C447.982 275.816 445.01 273.8 441.507 269.235C439.49 266.475 437.898 263.503 436.624 260.424C435.138 256.497 434.395 252.357 434.501 248.217C434.607 244.077 435.562 239.937 437.261 236.116C438.641 233.037 440.445 230.171 442.568 227.518C446.283 223.059 449.256 221.149 449.362 221.255C449.468 221.361 446.708 223.696 443.417 228.155C441.507 230.808 439.808 233.674 438.534 236.647C436.942 240.256 436.093 244.183 435.987 248.217C435.881 252.145 436.518 256.178 437.898 259.894C438.959 262.972 440.445 265.838 442.356 268.598C445.54 273.269 448.194 275.498 448.088 275.71Z" fill="#EBEBEB"/>
<path d="M449.999 221.361C450.105 221.255 453.077 223.272 456.58 227.836C458.597 230.49 460.189 233.462 461.463 236.647C462.949 240.574 463.692 244.714 463.586 248.854C463.48 252.994 462.525 257.134 460.826 260.955C459.446 264.034 457.642 266.9 455.519 269.554C451.803 274.012 448.831 275.923 448.725 275.817C448.619 275.71 451.273 273.375 454.669 268.917C456.686 266.263 458.279 263.397 459.552 260.425C461.145 256.815 461.994 252.888 462.1 248.854C462.206 244.926 461.569 240.893 460.189 237.177C459.128 234.099 457.536 231.233 455.731 228.473C452.546 223.802 449.893 221.467 449.999 221.361Z" fill="#EBEBEB"/>
<path d="M35.2655 236.859L36.6455 233.887C38.9808 228.473 36.5394 222.104 31.1256 219.769C25.7119 217.433 19.3428 219.875 17.0075 225.289L15.6275 228.367C12.7615 227.624 9.78922 229.004 8.51541 231.764L0.66021 249.491C-0.613606 252.569 0.766361 256.072 3.7386 257.452L22.2089 265.414C25.2873 266.688 28.7903 265.308 30.1703 262.335L37.9193 244.608C39.087 241.636 38.0255 238.345 35.2655 236.859ZM19.8736 252.569L15.0968 250.446L17.326 247.262C16.3706 245.776 16.6891 243.865 18.1752 242.804C19.6613 241.848 21.572 242.167 22.6335 243.653C23.5889 245.139 23.2704 247.05 21.7843 248.111C21.3597 248.323 20.9351 248.536 20.5105 248.642C20.2982 250.128 19.8736 252.569 19.8736 252.569ZM33.1425 235.904L17.6444 229.11L19.0244 226.138C21.0413 221.892 26.0304 220.087 30.2764 221.998C34.3102 223.909 36.2209 228.685 34.5225 232.825L33.1425 235.904Z" fill="#EBEBEB"/>
<path d="M445.859 17.975L447.239 15.0028C449.574 9.58906 447.133 3.21999 441.719 0.884661C436.305 -1.45067 429.936 0.990809 427.601 6.40453L426.221 9.48292C423.355 8.73986 420.383 10.1198 419.109 12.8798L411.466 30.607C410.192 33.6854 411.572 37.1884 414.544 38.5684L433.015 46.5297C436.093 47.8035 439.596 46.4236 440.976 43.4513L448.725 25.7241C449.786 22.858 448.619 19.4611 445.859 17.975ZM430.573 33.6854L425.796 31.5624L428.025 28.3778C427.07 26.8917 427.389 24.981 428.875 23.9195C430.361 22.858 432.271 23.2826 433.333 24.7687C434.288 26.2548 433.97 28.1655 432.484 29.2271C432.059 29.4394 431.635 29.6517 431.21 29.7578C430.892 31.2439 430.573 33.6854 430.573 33.6854ZM443.736 17.0197L428.238 10.3321L429.618 7.35989C431.635 3.11384 436.624 1.30926 440.87 3.21999C444.903 5.13071 446.814 9.90752 445.116 14.0474L443.736 17.0197Z" fill="#EBEBEB"/>
<path d="M392.252 47.0606C392.359 47.1667 379.09 58.1003 362.742 71.4754C346.289 84.8505 332.914 95.5717 332.914 95.4656C332.914 95.3594 346.077 84.4258 362.424 71.0508C378.771 57.6757 392.146 46.9544 392.252 47.0606Z" fill="#6C7A87"/>
<path d="M414.013 225.289C414.013 225.395 396.817 219.344 375.799 211.595C354.781 203.952 337.691 197.583 337.797 197.371C337.903 197.265 354.993 203.315 376.011 211.064C397.029 218.813 414.12 225.182 414.013 225.289Z" fill="#6C7A87"/>
<path d="M59.7865 78.2692C59.8926 78.163 78.2568 87.8228 100.973 100.03C123.69 112.238 141.948 122.11 141.841 122.216C141.735 122.322 123.371 112.662 100.655 100.455C77.9384 88.2474 59.6803 78.3753 59.7865 78.2692Z" fill="#6C7A87"/>
<path d="M66.3679 235.798C66.3679 235.691 81.4414 229.853 100.23 222.847C119.019 215.841 134.199 210.215 134.199 210.321C134.199 210.427 119.125 216.266 100.336 223.272C81.5475 230.278 66.474 236.01 66.3679 235.798Z" fill="#6C7A87"/>
<path d="M246.082 267.749C195.236 267.749 153.943 226.456 153.943 175.61C153.943 124.763 195.236 83.4702 246.082 83.4702C296.929 83.4702 338.221 124.763 338.221 175.61C338.115 226.456 296.929 267.643 246.082 267.749ZM246.082 84.5317C195.766 84.5317 155.004 125.294 155.004 175.61C155.004 225.925 195.766 266.687 246.082 266.687C296.398 266.687 337.16 225.925 337.16 175.61C337.054 125.294 296.398 84.5317 246.082 84.5317Z" fill="#FE5E00"/>
<path d="M362.53 174.23C362.53 174.23 362.53 173.593 362.424 172.319C362.424 171.682 362.424 170.939 362.318 169.984C362.212 169.028 362.318 167.967 362.212 166.799C362.105 165.632 361.999 164.252 361.893 162.766C361.787 161.279 361.469 159.687 361.256 157.883C360.726 154.38 359.982 150.346 358.921 145.781C357.647 140.792 356.055 135.909 354.144 131.132C351.915 125.506 349.155 119.987 346.077 114.785C338.434 102.047 328.349 90.795 316.46 81.7721C309.561 76.5707 302.13 72.1123 294.275 68.5032C285.783 64.5756 276.76 61.7095 267.525 60.0111C252.451 57.2511 236.953 57.3573 221.986 60.5418C216.678 61.7095 211.477 63.1956 206.488 65.0002C184.833 72.8554 166.044 86.9735 152.457 105.55C148.954 110.433 145.769 115.528 143.009 120.836C142.372 122.216 141.629 123.49 140.992 124.87L139.082 129.009C137.914 131.876 136.852 134.635 135.897 137.608C133.88 143.552 132.394 149.497 131.439 155.653C130.483 161.81 129.953 168.073 129.953 174.336C129.953 180.599 130.483 186.862 131.439 193.019C132.394 199.175 133.88 205.12 135.897 210.958C136.852 213.93 138.02 216.69 139.082 219.556L140.992 223.696C141.629 225.076 142.372 226.456 143.009 227.73C145.769 233.038 148.954 238.133 152.457 243.016C166.044 261.592 184.833 275.711 206.488 283.566C211.583 285.37 216.784 286.856 222.092 287.918C237.059 290.996 252.557 291.209 267.631 288.449C285.358 285.158 302.13 277.621 316.567 266.688C328.456 257.665 338.434 246.413 346.077 233.675C349.261 228.473 351.915 222.953 354.144 217.327C356.055 212.551 357.647 207.668 358.921 202.678C359.982 198.22 360.832 194.186 361.256 190.577C361.469 188.773 361.681 187.18 361.893 185.694C362.105 184.208 362.105 182.828 362.212 181.66C362.318 180.493 362.318 179.431 362.318 178.476C362.318 177.521 362.318 176.778 362.424 176.141C362.424 174.973 362.53 174.336 362.53 174.23C362.53 174.23 362.53 174.867 362.53 176.141C362.53 176.778 362.53 177.521 362.53 178.476C362.53 179.431 362.53 180.493 362.424 181.66C362.318 182.828 362.212 184.208 362.105 185.694C361.999 187.18 361.681 188.879 361.575 190.577C361.044 194.08 360.301 198.114 359.239 202.678C357.966 207.668 356.373 212.551 354.463 217.327C352.233 223.06 349.473 228.579 346.395 233.781C338.752 246.625 328.668 257.877 316.779 267.006C302.342 278.046 285.464 285.583 267.631 288.873C257.759 290.784 247.568 291.315 237.484 290.572C232.176 290.253 226.975 289.51 221.88 288.449C216.572 287.281 211.371 285.795 206.275 283.99C184.514 276.135 165.619 261.911 151.926 243.334C148.317 238.451 145.132 233.356 142.372 228.049C141.735 226.669 140.886 225.395 140.355 223.909C139.825 222.423 139.082 221.149 138.445 219.769C137.277 216.903 136.109 214.037 135.26 211.171C133.243 205.332 131.757 199.282 130.802 193.125C128.785 180.705 128.785 167.967 130.802 155.547C131.757 149.39 133.243 143.34 135.26 137.502C136.215 134.529 137.383 131.769 138.445 128.903C139.082 127.523 139.718 126.143 140.355 124.763C140.992 123.383 141.735 122.003 142.372 120.623C145.132 115.316 148.317 110.114 151.926 105.338C165.619 86.6551 184.514 72.5369 206.275 64.6817C211.371 62.8772 216.572 61.2849 221.88 60.2234C236.953 57.145 252.451 56.9327 267.631 59.6926C276.866 61.391 285.889 64.2571 294.487 68.1847C302.448 71.7939 309.879 76.2522 316.779 81.5598C328.668 90.6888 338.752 101.941 346.395 114.785C349.58 120.093 352.233 125.506 354.463 131.239C356.373 136.015 357.966 140.898 359.239 145.887C360.301 150.346 361.044 154.486 361.575 157.989C361.787 159.793 361.999 161.386 362.105 162.872C362.212 164.358 362.318 165.738 362.424 167.012C362.53 168.285 362.53 169.241 362.53 170.196C362.53 171.152 362.53 171.895 362.53 172.531C362.53 173.593 362.53 174.23 362.53 174.23Z" fill="#6C7A87"/>
<path d="M247.462 306.707V305.645C321.556 305.645 381.531 245.563 381.531 171.47C381.531 97.3761 321.45 37.4006 247.356 37.4006C173.262 37.4006 113.287 97.4823 113.287 171.47H112.225C112.225 96.9515 172.838 36.3391 247.356 36.3391C321.98 36.976 381.956 98.013 381.319 172.744C380.788 246.519 321.131 306.07 247.462 306.707Z" fill="#FE5E00"/>
<path d="M246.082 293.438V288.13C308.924 288.13 359.876 237.177 359.876 174.336C359.876 111.494 308.924 60.5417 246.082 60.5417V55.2341C311.896 55.2341 365.184 108.522 365.29 174.336C365.396 240.15 311.896 293.438 246.082 293.438Z" fill="#FE5E00"/>
<path d="M384.716 171.576H379.408C378.771 98.6502 319.114 40.1608 246.294 40.7977C174.324 41.4346 116.153 99.6056 115.516 171.576H110.208C110.208 95.8903 171.776 34.3225 247.462 34.3225C323.254 34.4287 384.609 95.7841 384.716 171.576Z" fill="#FE5E00"/>
<path d="M248.417 327.194C164.027 327.194 95.4534 258.62 95.4534 174.23C95.4534 89.8396 164.133 21.2659 248.417 21.2659V26.5734C166.999 26.5734 100.761 92.8119 100.761 174.23C100.761 255.648 166.999 321.886 248.417 321.886C329.835 321.886 396.074 255.648 396.074 174.23H401.382C401.382 258.514 332.808 327.194 248.417 327.194Z" fill="#FE5E00"/>
<path d="M246.082 345.133H243.428V344.072H246.082V345.133Z" fill="#FE5E00"/>
<path d="M232.601 344.497C230.796 344.39 228.992 344.178 227.293 343.966L227.399 342.904C229.204 343.117 231.009 343.223 232.707 343.435L232.601 344.497ZM216.678 342.586C214.874 342.267 213.069 341.949 211.371 341.524L211.583 340.463C213.281 340.781 215.086 341.206 216.784 341.524L216.678 342.586ZM200.968 338.977C199.269 338.552 197.571 338.021 195.766 337.491L196.085 336.429C197.783 336.96 199.482 337.491 201.18 337.915L200.968 338.977ZM185.576 333.988C183.877 333.351 182.179 332.714 180.587 331.971L181.011 331.015C182.604 331.652 184.302 332.395 186 333.032L185.576 333.988ZM170.715 327.512C169.122 326.663 167.53 325.92 165.938 325.071L166.469 324.116C168.061 324.965 169.653 325.814 171.245 326.557L170.715 327.512ZM156.703 319.657C155.217 318.702 153.624 317.746 152.138 316.791L152.775 315.942C154.261 316.897 155.747 317.959 157.233 318.808L156.703 319.657ZM143.328 310.528C141.948 309.467 140.461 308.299 139.082 307.237L139.718 306.388C141.098 307.45 142.478 308.617 143.858 309.679L143.328 310.528ZM131.014 300.125C129.74 298.958 128.36 297.684 127.086 296.41L127.829 295.667C129.103 296.941 130.377 298.108 131.651 299.382L131.014 300.125ZM119.656 288.555C118.488 287.175 117.32 285.901 116.153 284.521L117.002 283.884C118.17 285.264 119.337 286.644 120.505 287.918L119.656 288.555ZM109.571 276.135C108.51 274.649 107.448 273.163 106.493 271.783L107.342 271.146C108.298 272.632 109.359 274.012 110.421 275.498L109.571 276.135ZM100.549 262.654C99.5932 261.168 98.744 259.576 97.8948 257.983L98.8502 257.453C99.6994 259.045 100.655 260.531 101.504 262.123L100.549 262.654ZM93.0118 248.43C92.2688 246.837 91.5257 245.139 90.7827 243.547L91.738 243.122C92.4811 244.714 93.2241 246.413 93.9672 248.005L93.0118 248.43ZM86.7489 233.568C86.112 231.87 85.5812 230.172 84.9443 228.473L86.0058 228.155C86.5366 229.853 87.1735 231.552 87.8104 233.25L86.7489 233.568ZM81.9721 218.177C81.5475 216.478 81.1229 214.674 80.6983 212.975L81.7598 212.763C82.1844 214.461 82.609 216.266 83.0336 217.964L81.9721 218.177ZM78.6814 202.466C78.363 200.768 78.1507 198.963 77.9384 197.159L78.9999 197.052C79.2122 198.857 79.4245 200.555 79.7429 202.36L78.6814 202.466ZM76.8768 186.331C76.7707 184.527 76.6645 182.722 76.6645 180.917H77.726C77.726 182.722 77.8322 184.527 77.9384 186.225L76.8768 186.331ZM77.6199 170.302H76.5584C76.6645 168.498 76.6645 166.693 76.7707 164.889L77.8322 164.995C77.8322 166.693 77.726 168.498 77.6199 170.302ZM78.8937 154.38L77.8322 154.273C78.0445 152.469 78.2568 150.664 78.5753 148.966L79.6368 149.178C79.4245 150.77 79.106 152.575 78.8937 154.38ZM81.6536 138.563L80.5921 138.351C81.0167 136.652 81.4413 134.848 81.8659 133.149L82.9275 133.362C82.5029 135.06 82.0783 136.865 81.6536 138.563ZM85.8997 123.065L84.9443 122.746C85.4751 121.048 86.112 119.35 86.7489 117.651L87.7043 117.97C87.0674 119.668 86.5366 121.367 85.8997 123.065ZM91.6319 108.098L90.6765 107.673C91.4196 106.081 92.1626 104.382 92.9057 102.79L93.861 103.215C93.118 104.807 92.3749 106.505 91.6319 108.098ZM98.744 93.661L97.7886 93.1303C98.6379 91.538 99.5932 90.0519 100.442 88.4596L101.292 88.9904C100.549 90.5826 99.6994 92.1749 98.744 93.661ZM107.236 80.0737L106.387 79.4368C107.448 77.9506 108.51 76.4645 109.465 75.0845L110.315 75.7215C109.359 77.2076 108.298 78.5875 107.236 80.0737ZM117.002 67.3355L116.153 66.5924C117.32 65.2125 118.488 63.8325 119.656 62.5587L120.399 63.3018C119.337 64.5756 118.17 65.9555 117.002 67.3355ZM127.829 55.5527L127.086 54.8097C128.36 53.5358 129.634 52.262 131.014 51.0944L131.757 51.8374C130.377 53.1112 129.103 54.2789 127.829 55.5527ZM139.825 44.8314L139.188 43.9822C140.568 42.8145 141.948 41.753 143.434 40.6915L144.071 41.5407C142.585 42.6022 141.205 43.7699 139.825 44.8314ZM152.669 35.2778L152.032 34.4286C153.518 33.4732 155.004 32.5179 156.49 31.5625L157.021 32.4117C155.641 33.3671 154.155 34.3224 152.669 35.2778ZM166.469 27.1042L165.938 26.1488C167.53 25.2996 169.122 24.4504 170.715 23.7073L171.139 24.6627C169.547 25.4057 167.955 26.2549 166.469 27.1042ZM180.905 20.2043L180.481 19.249C182.179 18.5059 183.771 17.869 185.47 17.2321L185.894 18.1874C184.196 18.8243 182.497 19.5674 180.905 20.2043ZM195.872 14.7906L195.554 13.8352C197.252 13.3045 198.951 12.7737 200.755 12.3491L201.074 13.4106C199.375 13.7291 197.571 14.1537 195.872 14.7906ZM211.371 10.6507L211.158 9.58918C212.857 9.27073 214.661 8.84612 216.466 8.52767L216.678 9.58918C214.98 10.0138 213.175 10.3322 211.371 10.6507ZM227.187 8.20921L227.081 7.1477C228.886 6.9354 230.69 6.82925 232.389 6.61694L232.495 7.67846C230.796 7.78461 228.992 7.99691 227.187 8.20921Z" fill="#FE5E00"/>
<path d="M243.428 7.14769V6.08618H246.082V7.14769H243.428Z" fill="#FE5E00"/>
<path d="M186.531 165.419L290.241 123.914C297.035 121.154 304.784 124.551 307.438 131.345L339.389 211.383C342.149 218.177 338.752 225.926 331.959 228.579L228.249 270.085C221.455 272.845 213.706 269.448 211.052 262.654L179.101 182.616C176.447 175.928 179.738 168.179 186.531 165.419Z" fill="#FE5E00"/>
<path d="M297.565 174.973L283.872 180.386L254.149 106.081C247.144 88.5655 227.293 79.9673 209.778 86.9733C192.263 93.9793 183.665 113.83 190.671 131.345L220.393 205.65L206.7 211.064L176.977 136.758C166.999 111.707 179.207 83.258 204.258 73.2798C229.31 63.3015 257.759 75.5089 267.737 100.561L297.565 174.973Z" fill="#E0E0E0"/>
<g opacity="0.3">
<path d="M186.956 165.313L289.71 124.233C296.716 121.367 304.784 124.87 307.544 131.876L339.177 210.958C342.043 217.964 338.54 226.032 331.534 228.792L228.779 269.872C221.773 272.738 213.706 269.235 210.946 262.229L179.313 183.147C176.553 176.141 179.95 168.073 186.956 165.313Z" fill="black"/>
</g>
<path d="M180.587 167.861L275.167 130.071C281.961 127.417 289.604 130.708 292.364 137.395L324.422 217.433C327.075 224.227 323.785 231.87 317.097 234.63L222.516 272.42C215.723 275.073 208.08 271.783 205.32 265.095L173.262 185.057C170.608 178.157 173.899 170.514 180.587 167.861Z" fill="#FE5E00"/>
<path d="M261.156 139.306C261.262 139.518 245.339 146.206 225.701 154.38C206.063 162.553 190.034 169.028 190.034 168.816C190.034 168.604 205.851 161.916 225.489 153.743C245.127 145.569 261.049 139.094 261.156 139.306Z" fill="white"/>
<g opacity="0.3">
<path d="M261.686 185.376C258.502 177.308 249.373 173.381 241.305 176.565C233.238 179.75 229.31 188.879 232.495 196.946C235.042 203.528 241.73 207.562 248.736 206.818L255.848 224.652C256.697 226.881 259.245 227.943 261.474 226.987C263.703 226.138 264.765 223.59 263.915 221.467L256.803 203.634C262.217 199.388 264.234 191.957 261.686 185.376Z" fill="black"/>
</g>
<path d="M260.519 205.12C262.005 203.634 263.172 201.935 264.234 200.131C265.295 198.114 266.038 195.991 266.357 193.762C267.312 188.242 265.508 182.722 261.58 178.794C259.988 177.202 258.183 175.822 256.166 174.867C254.256 174.018 252.239 173.381 250.222 172.956C250.753 172.956 251.39 172.956 251.92 173.062C253.406 173.275 254.893 173.699 256.379 174.336C258.502 175.291 260.412 176.671 262.005 178.264C266.145 182.297 268.055 188.136 266.994 193.868C266.675 196.097 265.826 198.326 264.658 200.343C263.915 201.723 262.96 202.891 261.792 203.952C261.474 204.483 261.049 204.801 260.519 205.12Z" fill="#6C7A87"/>
<path d="M235.148 205.12C234.83 204.908 234.511 204.695 234.193 204.483C233.344 203.74 232.601 202.997 231.964 202.148C227.399 196.416 226.869 188.454 230.69 182.085C231.221 181.13 231.858 180.281 232.601 179.538C232.813 179.219 233.132 179.007 233.45 178.794C233.556 178.901 232.388 180.068 231.115 182.404C227.718 188.56 228.142 196.203 232.388 201.935C233.875 203.952 235.148 205.014 235.148 205.12Z" fill="white"/>
<path d="M243.64 209.685C243.64 209.897 242.579 209.897 241.411 209.685C240.244 209.472 239.288 209.154 239.288 208.941C239.288 208.729 240.35 208.729 241.517 208.941C242.685 209.154 243.64 209.578 243.64 209.685Z" fill="white"/>
<path d="M190.671 74.6599C189.928 75.1907 189.185 75.6153 188.336 75.9337C186.213 77.1014 184.196 78.4813 182.391 80.0736C167.742 92.0687 162.753 112.237 169.865 129.752C170.502 131.345 170.927 132.194 170.821 132.194C170.715 131.982 170.608 131.769 170.502 131.557C170.29 131.132 169.972 130.602 169.653 129.752C168.592 127.523 167.849 125.188 167.212 122.853C163.072 106.718 168.91 89.7334 182.073 79.5429C183.984 78.0567 186 76.6768 188.23 75.6153C188.973 75.1907 189.61 74.9784 190.034 74.7661C190.246 74.7661 190.459 74.6599 190.671 74.6599Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,68 @@
<svg width="395" height="350" viewBox="0 0 395 350" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.8" clip-path="url(#clip0_401_14393)">
<path d="M57.8475 320.372C134.977 359.882 260.023 359.882 337.152 320.372C414.282 280.862 414.282 216.808 337.152 177.298C260.023 137.789 134.977 137.789 57.8475 177.298C-19.2825 216.808 -19.2825 280.862 57.8475 320.372Z" fill="#F9F9F9"/>
<path d="M310.093 257.25C314.316 259.696 322.623 258.852 328.645 255.369L363.352 235.179C369.374 231.696 370.816 226.89 366.585 224.435C362.353 221.989 354.037 222.824 348.015 226.307L313.343 246.506C307.321 249.989 305.869 254.803 310.101 257.241L310.093 257.25Z" fill="#EDEDED"/>
<path d="M101.174 275.873L65.868 255.457L101.166 235.031L101.174 275.873Z" fill="#EDEDED"/>
<path d="M200.932 288.088L144.041 255.16L200.95 222.293L200.932 288.088Z" fill="#EDEDED"/>
<path d="M50.3134 263.649C49.3315 263.649 48.7406 262.831 48.7406 261.464V115.012L227.74 11.4751V157.335C227.74 159.572 226.167 162.306 224.23 163.42L51.7386 263.205C51.2346 263.501 50.7392 263.649 50.3134 263.649Z" fill="#FAFAFA"/>
<path d="M227.227 12.3543V157.334C227.227 159.372 225.742 161.958 223.978 162.985L51.4866 262.77C51.0608 263.014 50.6523 263.144 50.3134 263.144C49.3489 263.144 49.2446 261.977 49.2446 261.472V115.317L227.227 12.3543ZM228.244 10.5869L48.2279 114.725V261.472C48.2279 263.179 49.0795 264.163 50.3134 264.163C50.8174 264.163 51.391 263.997 51.9906 263.649L224.482 163.864C226.558 162.663 228.244 159.737 228.244 157.334V10.5869Z" fill="#E0E0E0"/>
<path d="M48.2279 114.725V139.225L228.244 35.0956V10.5869L48.2279 114.725Z" fill="#FE5E00"/>
<path d="M62.3488 116.031V119.549C62.3488 120.263 62.8615 120.559 63.4784 120.202L118.823 88.1879V82.0586L63.4784 114.072C62.8615 114.429 62.3488 115.317 62.3488 116.031Z" fill="#FAFAFA"/>
<path d="M124.75 78.6282C125.375 78.2712 125.879 78.5586 125.879 79.2812V82.7986C125.879 83.5212 125.375 84.4006 124.75 84.7576L118.815 88.1879V82.0586L124.75 78.6282Z" fill="#22334D"/>
<path d="M48.2279 106.829C48.2279 104.426 49.9137 101.5 51.9906 100.299L224.482 0.513913C226.558 -0.68758 228.244 0.287545 228.244 2.69053V10.5873L48.2279 114.725V106.829Z" fill="#22334D"/>
<path d="M208.24 14.0088C207.267 14.5747 206.476 15.9416 206.476 17.0734C206.476 18.2053 207.267 18.658 208.24 18.0921C209.214 17.5262 210.004 16.1593 210.004 15.0274C210.004 13.8956 209.214 13.4429 208.24 14.0088Z" fill="#4FAE80"/>
<path d="M213.828 10.77C212.855 11.3359 212.064 12.7028 212.064 13.8347C212.064 14.9665 212.855 15.4193 213.828 14.8533C214.801 14.2874 215.592 12.9205 215.592 11.7887C215.592 10.6568 214.801 10.2041 213.828 10.77Z" fill="#FFDC2E"/>
<path d="M219.424 7.53123C218.451 8.09715 217.66 9.46407 217.66 10.5959C217.66 11.7278 218.451 12.1805 219.424 11.6146C220.397 11.0486 221.188 9.68173 221.188 8.54989C221.188 7.41805 220.397 6.96531 219.424 7.53123Z" fill="#DD5F5F"/>
<g opacity="0.1">
<path d="M171.77 51.0461C169.641 52.2824 167.911 55.2774 167.911 57.7414C167.911 60.2053 169.641 61.2065 171.77 59.9702C173.899 58.7339 175.628 55.7389 175.628 53.2749C175.628 50.811 173.899 49.8098 171.77 51.0461Z" fill="black"/>
<path d="M154.121 61.259C151.992 62.4953 150.263 65.4903 150.263 67.9542C150.263 70.4182 151.992 71.4194 154.121 70.1831C156.25 68.9468 157.979 65.9518 157.979 63.4878C157.979 61.0239 156.25 60.0226 154.121 61.259Z" fill="black"/>
<path d="M189.419 40.842C187.29 42.0783 185.56 45.0733 185.56 47.5372C185.56 50.0012 187.29 51.0024 189.419 49.7661C191.548 48.5298 193.277 45.5348 193.277 43.0708C193.277 40.6069 191.548 39.6057 189.419 40.842Z" fill="black"/>
<path d="M207.067 30.612C204.938 31.8483 203.209 34.8433 203.209 37.3073C203.209 39.7712 204.938 40.7724 207.067 39.5361C209.196 38.2998 210.926 35.3048 210.926 32.8409C210.926 30.3769 209.196 29.3757 207.067 30.612Z" fill="black"/>
</g>
<path d="M66.1114 141.132L108.005 116.902C110.081 115.7 111.767 116.675 111.767 119.078V159.38C111.767 161.783 110.081 164.709 108.005 165.91L66.1114 190.14C64.0346 191.342 62.3488 190.367 62.3488 187.964V147.662C62.3488 145.259 64.0346 142.333 66.1114 141.132Z" fill="#C0D1D9"/>
<path d="M126.114 106.428L213.889 55.6516C215.966 54.4501 217.651 55.4252 217.651 57.8282V98.1305C217.651 100.533 215.966 103.459 213.889 104.66L126.114 155.436C124.037 156.638 122.351 155.663 122.351 153.26V112.958C122.351 110.555 124.037 107.629 126.114 106.428Z" fill="#C0D1D9"/>
<path d="M66.1114 202.39L132.709 163.864C134.786 162.663 136.472 163.516 136.472 165.771C136.472 168.026 134.786 170.829 132.709 172.031L66.1114 210.557C64.0346 211.759 62.3488 210.905 62.3488 208.65C62.3488 206.395 64.0346 203.592 66.1114 202.39Z" fill="#C0D1D9"/>
<path d="M66.1114 216.686L132.709 178.16C134.786 176.959 136.472 177.812 136.472 180.067C136.472 182.322 134.786 185.125 132.709 186.327L66.1114 224.853C64.0346 226.055 62.3488 225.201 62.3488 222.946C62.3488 220.691 64.0346 217.888 66.1114 216.686Z" fill="#C0D1D9"/>
<path d="M97.6464 182.514C97.6464 181.068 98.6544 179.318 99.9057 178.596L215.401 111.782C216.643 111.06 218.668 111.06 219.919 111.782L222.466 113.254C223.708 113.976 224.725 115.726 224.725 117.172V205.873C224.725 207.31 223.708 209.068 222.466 209.791L106.97 276.604C105.728 277.318 103.694 277.318 102.452 276.604L99.9057 275.133C98.6631 274.419 97.6464 272.652 97.6464 271.215V182.514Z" fill="#FE5E00"/>
<path opacity="0.4" d="M98.2981 180.284L104.702 183.985L224.047 114.942C223.639 114.237 223.074 113.619 222.457 113.253L219.902 111.791C218.659 111.059 216.635 111.059 215.392 111.791L99.897 178.595C99.2627 178.961 98.6978 179.579 98.2981 180.284Z" fill="white"/>
<path opacity="0.1" d="M97.6377 182.514V271.215C97.6377 272.66 98.6544 274.41 99.9057 275.133L102.452 276.613C102.643 276.718 102.843 276.813 103.069 276.892C103.356 276.988 103.668 277.066 103.999 277.109C104.589 277.188 105.206 277.162 105.78 277.049C105.928 277.014 106.075 276.97 106.214 276.927C106.31 276.892 106.397 276.866 106.484 276.822C106.067 276.97 105.702 276.953 105.406 276.805C104.981 276.578 104.711 276.065 104.711 275.316V186.614C104.711 185.683 105.18 184.978 105.884 184.69L104.711 184.002L98.3068 180.302C97.8984 181.007 97.6464 181.808 97.6464 182.531L97.6377 182.514Z" fill="black"/>
<path opacity="0.2" d="M104.711 183.985L158.196 204.01C162.011 205.429 166.295 204.018 168.511 200.605L177.097 187.424L224.473 114.699L178.808 187.92L224.708 205.916C224.699 206.648 224.429 207.457 224.004 208.171L178.687 188.12L168.702 204.123C166.408 207.797 161.768 209.225 157.823 207.475L150.715 204.332L105.406 276.787C104.981 276.561 104.711 276.047 104.711 275.298V273.818L150.497 204.236L104.711 183.985Z" fill="black"/>
<path d="M200.011 82.1541V285.798C200.011 288.201 201.697 289.176 203.774 287.975L295.086 235.153C297.162 233.951 298.848 231.026 298.848 228.623V24.9874C298.848 22.5844 297.162 21.6093 295.086 22.8108L203.774 75.6329C201.697 76.8344 200.011 79.7598 200.011 82.1628V82.1541Z" fill="#FAFAFA" stroke="#E0E0E0" stroke-width="1.17" stroke-miterlimit="10"/>
<path d="M205.303 85.95C205.303 85.3841 205.738 84.6789 206.285 84.3654L216.687 78.3493C217.226 78.0358 217.669 78.2448 217.669 78.802C217.669 79.3592 217.234 80.0731 216.687 80.3866L206.285 86.4027C205.747 86.7162 205.303 86.5072 205.303 85.95Z" fill="#22334D"/>
<path d="M205.303 90.0076C205.303 89.4417 205.738 88.7365 206.285 88.423L216.687 82.4069C217.226 82.0934 217.669 82.3024 217.669 82.8596C217.669 83.4168 217.234 84.1308 216.687 84.4442L206.285 90.4604C205.747 90.7738 205.303 90.5648 205.303 90.0076Z" fill="#22334D"/>
<path d="M205.303 94.0648C205.303 93.4988 205.738 92.7936 206.285 92.4802L216.687 86.464C217.226 86.1506 217.669 86.3595 217.669 86.9167C217.669 87.474 217.234 88.1879 216.687 88.5013L206.285 94.5175C205.747 94.8309 205.303 94.622 205.303 94.0648Z" fill="#22334D"/>
<path d="M291.783 41.8431V40.215C291.783 40.1193 291.714 40.0757 291.627 40.1105L290.376 40.6765C290.289 40.72 290.193 40.6765 290.167 40.5807C290.098 40.3543 290.002 40.1454 289.889 39.9625C289.837 39.8755 289.828 39.7449 289.872 39.6578L290.706 37.9688C290.749 37.8817 290.715 37.7685 290.628 37.725L289.724 37.2026C289.637 37.1503 289.533 37.1765 289.472 37.2635L288.429 38.8307C288.377 38.9091 288.255 38.97 288.16 38.97C287.943 38.97 287.717 38.9874 287.482 39.0396C287.386 39.0571 287.308 39.0048 287.291 38.9091L287.152 37.5421C287.143 37.4464 287.065 37.4028 286.987 37.4551L285.579 38.2648C285.492 38.317 285.423 38.4302 285.414 38.526L285.275 40.0931C285.266 40.1889 285.197 40.3195 285.127 40.3892C284.858 40.6416 284.588 40.9202 284.345 41.2163C284.284 41.2946 284.163 41.3295 284.067 41.2946L283.163 40.9812C283.068 40.9464 282.955 40.9899 282.911 41.077L281.877 42.8705C281.825 42.9576 281.851 43.0707 281.921 43.1404L282.642 43.7673C282.711 43.8282 282.746 43.9588 282.711 44.0459C282.581 44.4115 282.468 44.7772 282.39 45.1342C282.372 45.2299 282.286 45.3518 282.207 45.4041L280.921 46.3095C280.843 46.3618 280.774 46.4924 280.774 46.5882V48.2163C280.774 48.312 280.843 48.3556 280.93 48.3207L282.181 47.7548C282.268 47.7113 282.364 47.7548 282.39 47.8506C282.459 48.077 282.555 48.2859 282.668 48.4688C282.72 48.5558 282.729 48.6864 282.685 48.7735L281.851 50.4538C281.808 50.5409 281.842 50.6541 281.929 50.6976L282.833 51.22C282.92 51.2722 283.024 51.2461 283.085 51.1591L284.128 49.5919C284.18 49.5135 284.302 49.4526 284.397 49.4526C284.614 49.4526 284.84 49.4352 285.075 49.3829C285.171 49.3655 285.249 49.4178 285.266 49.5135L285.405 50.8804C285.414 50.9762 285.492 51.0197 285.57 50.9675L286.978 50.1578C287.065 50.1056 287.134 49.9924 287.143 49.8966L287.282 48.3294C287.291 48.2337 287.36 48.1031 287.43 48.0334C287.699 47.7809 287.969 47.5023 288.212 47.2063C288.273 47.128 288.394 47.0931 288.49 47.128L289.394 47.4414C289.489 47.4762 289.602 47.4327 289.646 47.3456L290.68 45.5521C290.732 45.465 290.706 45.3518 290.636 45.2822L289.915 44.6553C289.846 44.5944 289.811 44.4638 289.846 44.3767C289.976 44.011 290.089 43.6454 290.167 43.2884C290.185 43.1926 290.271 43.0707 290.35 43.0185L291.636 42.113C291.714 42.0608 291.783 41.9302 291.783 41.8344V41.8431ZM286.283 46.9538C284.971 47.7113 283.911 47.0931 283.911 45.5782C283.911 44.0633 284.971 42.2175 286.283 41.46C287.595 40.7026 288.655 41.3207 288.655 42.8357C288.655 44.3506 287.595 46.1964 286.283 46.9538Z" fill="#22334D"/>
<path d="M249.43 81.9802C260.101 75.8073 268.747 80.8135 268.747 93.1506C268.747 105.488 260.101 120.506 249.43 126.671C238.759 132.843 230.113 127.837 230.113 115.491C230.113 103.146 238.759 88.1357 249.43 81.9715V81.9802Z" fill="#EDEDED"/>
<path d="M249.291 91.6964C252.749 89.6939 255.556 91.3133 255.556 95.3183C255.556 99.3233 252.749 104.19 249.291 106.193C245.832 108.195 243.026 106.576 243.026 102.571C243.026 98.5658 245.832 93.6989 249.291 91.6964Z" fill="#FE5E00"/>
<path d="M261.639 113.045C261.639 106.463 257.025 103.799 251.342 107.09L247.787 109.144C242.104 112.435 237.49 120.437 237.49 127.01V128.804C238.515 129.143 239.628 129.317 240.81 129.317C243.399 129.317 246.328 128.464 249.421 126.67C254.053 123.989 258.311 119.636 261.639 114.56V113.036V113.045Z" fill="#FE5E00"/>
<path d="M231.989 145.041L266.861 124.868C267.904 124.268 268.747 124.694 268.747 125.826C268.747 126.958 267.904 128.351 266.861 128.96L231.989 149.133C230.947 149.734 230.104 149.307 230.104 148.176C230.104 147.044 230.947 145.651 231.989 145.041Z" fill="#FE5E00"/>
<path d="M228.244 159.468L270.598 134.942C271.641 134.341 272.484 134.767 272.484 135.899C272.484 137.031 271.641 138.424 270.598 139.034L228.244 163.56C227.201 164.16 226.359 163.734 226.359 162.602C226.359 161.47 227.201 160.077 228.244 159.468Z" fill="#22334D"/>
<path d="M214.358 196.74L284.484 156.177C286.561 154.975 288.247 155.829 288.247 158.084C288.247 160.339 286.561 163.142 284.484 164.343L214.358 204.907C212.281 206.108 210.595 205.255 210.595 203C210.595 200.745 212.281 197.942 214.358 196.74Z" fill="#C0D1D9"/>
<path d="M214.358 213.073L284.484 172.51C286.561 171.308 288.247 172.162 288.247 174.417C288.247 176.672 286.561 179.475 284.484 180.677L214.358 221.24C212.281 222.441 210.595 221.588 210.595 219.333C210.595 217.078 212.281 214.275 214.358 213.073Z" fill="#C0D1D9"/>
<path d="M237.186 220.291L261.665 206.134C262.708 205.533 263.551 206.021 263.551 207.222V215.119C263.551 216.321 262.708 217.783 261.665 218.384L237.186 232.541C236.143 233.142 235.3 232.654 235.3 231.453V223.556C235.3 222.354 236.143 220.892 237.177 220.291H237.186Z" fill="#FE5E00"/>
<path d="M215.896 197.941C214.923 198.507 214.132 199.874 214.132 201.006C214.132 202.138 214.923 202.591 215.896 202.025C216.869 201.459 217.66 200.092 217.66 198.96C217.66 197.828 216.869 197.375 215.896 197.941Z" fill="#FAFAFA"/>
<path d="M221.484 194.703C220.51 195.269 219.72 196.635 219.72 197.767C219.72 198.899 220.51 199.352 221.484 198.786C222.457 198.22 223.248 196.853 223.248 195.721C223.248 194.589 222.457 194.137 221.484 194.703Z" fill="#FAFAFA"/>
<path d="M227.071 191.464C226.098 192.03 225.307 193.397 225.307 194.529C225.307 195.66 226.098 196.113 227.071 195.547C228.044 194.981 228.835 193.614 228.835 192.483C228.835 191.351 228.044 190.898 227.071 191.464Z" fill="#FAFAFA"/>
<path d="M232.667 188.225C231.694 188.791 230.903 190.158 230.903 191.29C230.903 192.422 231.694 192.874 232.667 192.308C233.641 191.742 234.431 190.376 234.431 189.244C234.431 188.112 233.641 187.659 232.667 188.225Z" fill="#FAFAFA"/>
<path d="M238.255 184.986C237.281 185.552 236.491 186.919 236.491 188.051C236.491 189.183 237.281 189.636 238.255 189.07C239.228 188.504 240.019 187.137 240.019 186.005C240.019 184.873 239.228 184.42 238.255 184.986Z" fill="#FAFAFA"/>
<path d="M243.851 181.748C242.878 182.313 242.087 183.68 242.087 184.812C242.087 185.944 242.878 186.397 243.851 185.831C244.824 185.265 245.615 183.898 245.615 182.766C245.615 181.634 244.824 181.182 243.851 181.748Z" fill="#FAFAFA"/>
<path d="M249.438 178.509C248.465 179.075 247.674 180.442 247.674 181.573C247.674 182.705 248.465 183.158 249.438 182.592C250.412 182.026 251.202 180.659 251.202 179.527C251.202 178.396 250.412 177.943 249.438 178.509Z" fill="#FAFAFA"/>
<path d="M215.896 214.292C214.923 214.858 214.132 216.225 214.132 217.357C214.132 218.489 214.923 218.942 215.896 218.376C216.869 217.81 217.66 216.443 217.66 215.311C217.66 214.179 216.869 213.727 215.896 214.292Z" fill="#FAFAFA"/>
<path d="M221.484 211.054C220.51 211.62 219.72 212.987 219.72 214.118C219.72 215.25 220.51 215.703 221.484 215.137C222.457 214.571 223.248 213.204 223.248 212.072C223.248 210.941 222.457 210.488 221.484 211.054Z" fill="#FAFAFA"/>
<path d="M227.071 207.815C226.098 208.381 225.307 209.748 225.307 210.88C225.307 212.011 226.098 212.464 227.071 211.898C228.044 211.332 228.835 209.965 228.835 208.834C228.835 207.702 228.044 207.249 227.071 207.815Z" fill="#FAFAFA"/>
<path d="M232.667 204.576C231.694 205.142 230.903 206.509 230.903 207.64C230.903 208.772 231.694 209.225 232.667 208.659C233.641 208.093 234.431 206.726 234.431 205.594C234.431 204.462 233.641 204.01 232.667 204.576Z" fill="#FAFAFA"/>
<path d="M238.255 201.337C237.281 201.903 236.491 203.27 236.491 204.402C236.491 205.533 237.281 205.986 238.255 205.42C239.228 204.854 240.019 203.487 240.019 202.356C240.019 201.224 239.228 200.771 238.255 201.337Z" fill="#FAFAFA"/>
<path d="M243.851 198.098C242.878 198.664 242.087 200.031 242.087 201.163C242.087 202.295 242.878 202.747 243.851 202.181C244.824 201.616 245.615 200.249 245.615 199.117C245.615 197.985 244.824 197.532 243.851 198.098Z" fill="#FAFAFA"/>
<path d="M249.438 194.859C248.465 195.425 247.674 196.792 247.674 197.924C247.674 199.056 248.465 199.509 249.438 198.943C250.412 198.377 251.202 197.01 251.202 195.878C251.202 194.746 250.412 194.293 249.438 194.859Z" fill="#FAFAFA"/>
<path d="M327.863 247.342C324.813 247.342 321.928 246.593 319.304 245.078C313.039 241.456 309.45 233.873 309.45 224.279C309.45 207.405 320.155 187.99 333.824 180.085C341.185 175.827 348.493 175.366 354.402 178.787C360.667 182.409 364.256 189.993 364.256 199.587C364.256 216.46 353.55 235.876 339.881 243.781C335.78 246.149 331.695 247.342 327.863 247.342ZM345.816 188.678C343.626 188.678 341.306 189.784 339.881 190.602C329.957 196.34 321.572 211.767 321.572 224.279C321.572 229.346 322.988 233.185 325.361 234.561C328.063 236.128 331.817 234.422 333.816 233.264C343.739 227.526 352.125 212.098 352.125 199.587C352.125 194.52 350.709 190.68 348.336 189.305C347.563 188.852 346.694 188.678 345.816 188.678Z" fill="#364860"/>
<path d="M354.297 202.286C354.297 192.805 348.336 189.305 348.336 189.305C350.708 190.68 352.125 194.52 352.125 199.587C352.125 212.098 343.739 227.517 333.816 233.264C331.817 234.422 328.063 236.128 325.361 234.561C325.361 234.561 330.392 237.687 337.335 233.891C347.398 228.388 354.289 214.806 354.289 202.295L354.297 202.286Z" fill="#22334D"/>
<path d="M344.739 83.7998C344.739 80.3346 341.932 77.5225 338.473 77.5225C335.015 77.5225 332.208 80.3346 332.208 83.7998C332.208 83.7998 332.208 83.8085 332.208 83.8172V183.689C332.208 184.621 332.816 185.544 334.042 186.249C336.492 187.668 340.455 187.668 342.905 186.249C344.13 185.544 344.739 184.612 344.739 183.689V83.8172C344.739 83.8172 344.739 83.8085 344.739 83.7998Z" fill="#22334D"/>
<path d="M339.733 106.114C339.733 103.799 338.117 100.986 336.11 99.8371C335.093 99.245 334.181 99.2102 333.52 99.6107L318.053 108.535L317.679 108.761C317.627 108.787 317.575 108.813 317.531 108.848C317.262 109.031 317.018 109.231 316.793 109.458C315.854 110.389 315.281 111.678 315.281 113.097C315.281 115.047 316.358 116.736 317.948 117.616C318.678 118.007 319.504 118.234 320.399 118.234C321.294 118.234 322.119 118.007 322.841 117.616C322.936 117.563 323.04 117.502 323.136 117.45L338.699 108.465C339.342 108.082 339.742 107.272 339.742 106.123L339.733 106.114Z" fill="#364860"/>
<path d="M339.733 131.128C339.733 128.812 338.117 126 336.11 124.851C335.093 124.259 334.181 124.224 333.52 124.624L315.393 135.046L315.02 135.272C314.968 135.299 314.916 135.325 314.872 135.359C314.603 135.534 314.359 135.743 314.133 135.969C313.195 136.901 312.621 138.189 312.621 139.608C312.621 141.558 313.699 143.248 315.289 144.127C316.019 144.519 316.845 144.745 317.74 144.745C318.635 144.745 319.46 144.519 320.182 144.127C320.277 144.075 320.381 144.014 320.477 143.961L338.699 133.479C339.342 133.096 339.742 132.286 339.742 131.137L339.733 131.128Z" fill="#364860"/>
</g>
<defs>
<clipPath id="clip0_401_14393">
<rect width="395" height="350" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="287.47433"
height="292.50049"
viewBox="0 0 287.47433 292.50049"
fill="none"
version="1.1"
id="svg2705"
sodipodi:docname="logo.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2709" />
<sodipodi:namedview
id="namedview2707"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.4609375"
inkscape:cx="144.27119"
inkscape:cy="147.52542"
inkscape:window-width="1920"
inkscape:window-height="1148"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2705" />
<path
d="m 24.974336,57.579473 c 0,0 -29,40 -24.5,58.000997 -0.155,-0.698 19,-21.000997 19,-21.000997 1,5.499997 47.5,19.000997 47.5,19.000997 6.174,3.935 8.302,6.761 9.5,13 v 24 c 16.063,9.994 30.818004,13.31 66.500004,15.5 37.913,-1.616 53.572,-4.974 67.5,-15.5 0,0 -1.5,-18.501 0,-25.001 1.5,-6.5 4.829,-10.217 10,-11.999 19.293,-4.414 28.816,-9.243 47,-18.500997 l 20,20.500997 c -3.039,-22.539997 -8.262,-35.015997 -24,-56.999997 -12.494,-14.278 -20.505,-21.766 -37,-34 -17.932,-10.596 -27.882,-14.6409998 -45.5,-19.4999998 -30.58,-6.262 -47.438,-6.243 -77,0 -18.113004,5.4609998 -27.141004,9.5809998 -40.500004,19.4999998 0,0 -50.5,-29.0009998 -57,-24.00099984 -6.5,5.00000004 18.5,56.99999984 18.5,56.99999984 z"
fill="#ffffff"
id="path2701" />
<path
d="m 59.500336,212.50047 c -23.228,-10.168 -35.814,-17.849 -57.5,-35 5.214,21.587 10.076,32.953 22,52 12.706,17.195 20.513,24.868 35.5,35.5 16.208,11.124 25.928,15.987 44.500004,22 16.684,4.01 25.987,5.288 42.5,5.5 16.837,-0.843 25.574,-2.678 40,-6 15.912,-6.341 24.018,-10.339 37.5,-18 22.355,12.62 50,27 54,21 4,-6 -18,-54.5 -18,-54.5 14.673,-21.015 20.675,-35.167 26.5,-59 -20.086,15.757 -33.135,24.951 -58,36.5 -30.758,12.132 -48.672,16.028 -82,17 -38.827,-0.677 -57.775004,-4.519 -87.000004,-17 z"
fill="#ffffff"
id="path2703" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -1,13 +1,11 @@
@extends('layouts.main')
@extends('layouts.main', ['welcome' => true])
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<p class="text-center pt-3">Scan the following QR Code using an authenticated device and wait a few seconds.</p>
<p class="text-center pt-3"><img src="{{ route('auth_tokens.qrcode', ['token' => $authToken->token]) }}"></p>
<section>
<p>Scan the following QR Code using an authenticated device and wait a few seconds.</p><br />
<p><img src="{{ route('auth_tokens.qrcode', ['token' => $authToken->token]) }}"></p>
<script type="text/javascript">
setTimeout(function () { location.reload(1); }, 5000);
</script>
@endif
</section>
@endsection

View file

@ -1,18 +0,0 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
@if ($account->activated)
<p>A unique authentication link was sent by email to <b>{{ $account->email }}</b>.</p>
@include('parts.account_variables', ['account' => $account])
@else
<p>To finish your registration process and set a password please follow the link sent on your email address <b>{{ $account->email }}</b>.</p>
<script type="text/javascript">
setTimeout(function () { location.reload(1); }, 5000);
</script>
@endif
@endif
@endsection

View file

@ -1,25 +0,0 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
@if ($account->activated)
<p>Please enter the PIN code was sent to your phone number to finish your authentication.</p>
@else
<p>To finish your registration process and set a password please enter the PIN code you just received by SMS.</p>
@endif
<div class="card mt-1">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate.phone_confirm']) !!}
<div class="form-group">
{!! Form::label('code', 'Code') !!}
{!! Form::hidden('account_id', $account->id) !!}
{!! Form::text('code', old('code'), ['class' => 'form-control', 'placeholder' => '1234', 'required']) !!}
</div>
{!! Form::submit('Login', ['class' => 'btn btn-primary btn-centered']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -1,4 +1,4 @@
@extends('layouts.main')
@extends('layouts.main', ['welcome' => true])
@section('content')
<div class="card mt-3">

View file

@ -1,4 +1,4 @@
@extends('layouts.main')
@extends('layouts.main', ['welcome' => true])
@section('content')
<h3 class="text-center mt-5">Thanks for the validation</h3>

View file

@ -1,11 +1,11 @@
@extends('layouts.account')
@extends('layouts.main')
@section('content')
<h2>Manage your account</h2>
<h1><i class="material-icons">dashboard</i> Dashboard</h1>
<div class="list-group mb-3 pt-2">
<a href="{{ route('account.email') }}" class="list-group-item list-group-item-action">
<a href="{{ route('account.email.change') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Change my current account email</h5>
</div>

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('content')

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item active" aria-current="page">Devices</li>

View file

@ -48,11 +48,11 @@ If you forgot your password or didn't configured it, you can always recover your
# Account panel
Once authenticated you will get access to @if (config('app.web_panel')) [your account panel]({{ route('account.panel') }}) @else your account panel @endif.
Once authenticated you will get access to @if (config('app.web_panel')) [your account panel]({{ route('account.dashboard') }}) @else your account panel @endif.
## Change your email address
You can @if (config('app.web_panel')) [change your email address]({{ route('account.email') }}) @else change your email address @endif from the panel. A confirmation email containing a unique link will be sent to validate the new one.
You can @if (config('app.web_panel')) [change your email address]({{ route('account.email.change') }}) @else change your email address @endif from the panel. A confirmation email containing a unique link will be sent to validate the new one.
## Change your password

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('content')
@ -12,12 +12,12 @@
{!! Form::open(['route' => 'account.email.request_update']) !!}
<div class="form-group">
{!! Form::label('email', 'New email') !!}
{!! Form::email('email', old('email'), ['class' => 'form-control', 'placeholder' => 'bob@example.net', 'required']) !!}
{!! Form::label('email', 'New email') !!}
</div>
<div class="form-group">
{!! Form::label('email_confirmation', 'Email confirmation') !!}
{!! Form::email('email_confirmation', old('email_confirm'), ['class' => 'form-control', 'placeholder' => 'bob@example.net', 'required']) !!}
{!! Form::label('email_confirmation', 'Email confirmation') !!}
</div>
{!! Form::hidden('email_current', $account->email) !!}

View file

@ -0,0 +1,48 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<section>
<h1>
<i class="material-icons">mail</i>
@if ($account->email)
Change your email
@else
Set your email
@endif
</h1>
{!! Form::open(['route' => 'account.email.request_change']) !!}
<div class="large">
@if ($account->email)
<p>Please enter the new email address that you would like to link to your account.</p>
@else
<p>The verification code is invalid.</p>
<p>Please enter again your email address to receive a new code.</p>
@endif
</div>
<div class="large">
{!! Form::email('email', null, ['placeholder' => 'email@server.tld', 'required']) !!}
{!! Form::label('email', 'Email') !!}
@include('parts.errors', ['name' => 'email'])
</div>
@include('parts.captcha')
<div class="large">
{!! Form::submit('Verify', ['class' => 'btn oppose']) !!}
</div>
{!! Form::close() !!}
</section>
<section class="on_desktop">
<img src="/img/lock.svg">
</section>
@endsection
@section('footer')
Hop
@endsection

View file

@ -0,0 +1,41 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<section>
<h1><i class="material-icons">account_circle</i> Validate your email</h1>
{!! Form::open(['route' => 'account.email.update']) !!}
<div class="large">
<p>A verification code was sent by email on <b>{{ $emailChangeCode->email }}</b>.</p>
<p>Please enter the verification code below:</p>
</div>
<div>
{!! Form::number('code', null, ['placeholder' => '0000', 'required', 'min' => 0000, 'max' => 9999]) !!}
{!! Form::label('code', 'Code') !!}
@include('parts.errors', ['name' => 'code'])
</div>
<div>
{!! Form::submit('Validate', ['class' => 'btn']) !!}
</div>
{!! Form::close() !!}
<div class="large" style="margin-top: 2rem;">
<p>
You didn't receive the code?
<a class="btn btn-secondary" href="{{ route('account.email.change') }}">Resend a code</a>
</p>
</div>
</section>
<section class="on_desktop">
<img src="/img/lock.svg">
</section>
@endsection
@section('footer')
Hop
@endsection

View file

@ -1,36 +0,0 @@
@extends('layouts.main')
@section('content')
<h2>{{ config('app.name') }}</h2>
<p>There are <b>{{ number_format($count) }} users</b> registered with this service.</p>
@if (config('instance.intro_registration'))
@parsedown(config('instance.intro_registration'))
@endif
@if (config('app.web_panel'))
<hr />
<div class="list-group mb-3">
@if (publicRegistrationEnabled())
<a href="{{ route('account.register') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Create an account</h5>
</div>
<p class="mb-1">Register on our service</p>
</a>
@endif
<a href="{{ route('account.login') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Manage your account</h5>
</div>
<p class="mb-1">Get access to your account panel to configure it</p>
</a>
</div>
@endif
@include('parts.password_recovery')
@endsection

View file

@ -1,40 +1,55 @@
@extends('layouts.main')
@extends('layouts.main', ['welcome' => true])
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
{!! Form::open(['route' => 'account.authenticate']) !!}
<div class="form-row">
<div class="form-group col-md-6">
@if (config('app.phone_authentication'))
{!! Form::label('username', 'Username or phone number') !!}
{!! Form::text('username', old('username'), ['class' => 'form-control', 'placeholder' => 'username or phone number', 'required']) !!}
@else
{!! Form::label('username', 'Username') !!}
{!! Form::text('username', old('username'), ['class' => 'form-control', 'placeholder' => 'username', 'required']) !!}
@endif
</div>
<div class="form-group col-md-6">
{!! Form::label('password', 'Password') !!}
{!! Form::password('password', ['class' => 'form-control', 'placeholder' => 'myPassword', 'required']) !!}
</div>
<section>
<h1><i class="material-icons">waving_hand</i> Oh hi!</h1>
@if (config('instance.intro_registration'))
@parsedown(config('instance.intro_registration'))
@endif
@if (Auth::check())
@include('parts.already_auth')
@else
{!! Form::open(['route' => 'account.authenticate']) !!}
<div class="large">
@if (config('app.phone_authentication'))
{!! Form::text('username', old('username'), ['placeholder' => 'username or phone number', 'required']) !!}
{!! Form::label('username', 'Username or phone number') !!}
@else
{!! Form::text('username', old('username'), ['placeholder' => 'username', 'required']) !!}
{!! Form::label('username', 'Username') !!}
@endif
</div>
<div class="large">
{!! Form::password('password', ['placeholder' => 'myPassword', 'required']) !!}
{!! Form::label('password', 'Password') !!}
</div>
<div class="large">
{!! Form::submit('Login', ['class' => 'btn oppose']) !!}
</div>
{!! Form::submit('Login', ['class' => 'btn btn-primary btn-centered mt-1']) !!}
{!! Form::close() !!}
{!! Form::close() !!}
<br />
@include('parts.password_recovery')
@endif
@include('parts.recovery')
@endif
@if (publicRegistrationEnabled())
<hr />
@if (publicRegistrationEnabled())
<br />
<br />
<p class="text-center">
No account yet?
<a class="btn btn-secondary ml-2" href="{{ route('account.register') }}">Register</a>
</p>
@endif
<p>
No account yet?
<a class="btn btn-secondary" href="{{ route('account.register') }}">Register</a>
</p>
@endif
</section>
<section class="on_desktop" style="text-align: center;">
<span style="color: var(--main-5); font-size: 5rem; font-weight: 300;">{{ $count }}</span><br />
<p style="margin-bottom: 3rem;">users</p>
<img src="/img/login.svg">
</section>
@endsection
@endsection

View file

@ -1,33 +0,0 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<div class="card mt-3">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate.email']) !!}
<div class="form-group">
{!! Form::label('email', 'Email') !!}
{!! Form::email('email', old('email'), ['class' => 'form-control', 'placeholder' => 'bob@example.com', 'required']) !!}
</div>
@if (config('app.account_email_unique') == false)
<div class="form-group">
{!! Form::label('username', 'SIP Username') !!}
<div class=" input-group mb-3">
{!! Form::text('username', old('username'), ['class' => 'form-control', 'placeholder' => 'username', 'required']) !!}
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">{{ $domain }}</span>
</div>
</div>
</div>
@endif
@include('parts.captcha')
{!! Form::submit('Send the authentication link', ['class' => 'btn btn-primary btn-centered']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -1,20 +0,0 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<div class="card mt-3">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate.phone']) !!}
<div class="form-group">
{!! Form::label('phone', 'Phone') !!}
{!! Form::text('phone', old('phone'), ['class' => 'form-control', 'placeholder' => '+123456789', 'required']) !!}
</div>
@include('parts.captcha')
{!! Form::submit('Send the authentication code by SMS', ['class' => 'btn btn-primary btn-centered']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('content')

View file

@ -0,0 +1,47 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<section>
<h1>
<i class="material-icons">mail</i>
@if ($account->phone)
Change your phone number
@else
Set your phone number
@endif
</h1>
{!! Form::open(['route' => 'account.phone.request_change']) !!}
<div class="large">
@if ($account->phone)
<p>Please enter the new phone number that you would like to link to your account.</p>
@else
<p>The verification code is invalid or you didn't receive it.</p>
<p>Please enter your phone number again to receive a new code.</p>
@endif
</div>
<div class="large">
{!! Form::text('phone', null, ['placeholder' => '+12345678', 'required']) !!}
{!! Form::label('phone', 'Phone') !!}
@include('parts.errors', ['name' => 'phone'])
</div>
@include('parts.captcha')
<div class="large">
{!! Form::submit('Verify', ['class' => 'btn oppose']) !!}
</div>
{!! Form::close() !!}
</section>
<section class="on_desktop">
<img src="/img/lock.svg">
</section>
@endsection
@section('footer')
Hop
@endsection

View file

@ -0,0 +1,41 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<section>
<h1><i class="material-icons">account_circle</i> Validate your phone number</h1>
{!! Form::open(['route' => 'account.phone.update']) !!}
<div class="large">
<p>A verification code was sent by SMS on <b>{{ $phoneChangeCode->phone }}</b>.</p>
<p>Please enter the verification code below:</p>
</div>
<div>
{!! Form::number('code', null, ['placeholder' => '0000', 'required', 'min' => 0000, 'max' => 9999]) !!}
{!! Form::label('code', 'Code') !!}
@include('parts.errors', ['name' => 'code'])
</div>
<div>
{!! Form::submit('Validate', ['class' => 'btn']) !!}
</div>
{!! Form::close() !!}
<div class="large" style="margin-top: 2rem;">
<p>
You didn't receive the code?
<a class="btn btn-secondary" href="{{ route('account.phone.change') }}">Resend a code</a>
</p>
</div>
</section>
<section class="on_desktop">
<img src="/img/lock.svg">
</section>
@endsection
@section('footer')
Hop
@endsection

View file

@ -0,0 +1,22 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<section>
<h1><i class="material-icons">account_circle</i> Account recovery</h1>
{!! Form::open(['route' => 'account.recovery.confirm']) !!}
<p class="large">Enter the pin code you received to recover your account.</p>
<div class="large">
{!! Form::text('code', old('code'), ['placeholder' => '1234', 'required']) !!}
{!! Form::label('code', 'Code') !!}
{!! Form::hidden('account_id', $account_id) !!}
</div>
<div class="large">
{!! Form::submit('Login', ['class' => 'btn oppose']) !!}
</div>
{!! Form::close() !!}
</section>
<section class="on_desktop">
<img src="/img/lock.svg">
</section>
@endsection

View file

@ -0,0 +1,47 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<section>
<h1><i class="material-icons">account_circle</i> Account recovery</h1>
<div>
{!! Form::open(['route' => 'account.recovery.send']) !!}
@if ($method == 'email')
<p class="large">Enter your email account to recover it.</p>
<div class="large">
{!! Form::email('email', old('email'), ['placeholder' => 'bob@example.com', 'required']) !!}
{!! Form::label('email', 'Email') !!}
@include('parts.errors', ['name' => 'email'])
@include('parts.errors', ['name' => 'identifier'])
</div>
@if (config('app.account_email_unique') == false)
<div>
{!! Form::text('username', old('username'), ['placeholder' => 'username', 'required']) !!}
{!! Form::label('username', 'Username') !!}
</div>
<div>
{!! Form::text('username', $domain, ['disabled']) !!}
</div>
@endif
@elseif($method == 'phone')
<p class="large">Enter your phone number to recover your account.</p>
<div>
{!! Form::text('phone', old('phone'), ['placeholder' => '+123456789', 'required']) !!}
{!! Form::label('phone', 'Phone') !!}
@include('parts.errors', ['name' => 'phone'])
@include('parts.errors', ['name' => 'identifier'])
</div>
@endif
@include('parts.captcha')
<div class="large">
{!! Form::submit('Send the code', ['class' => 'btn oppose']) !!}
</div>
{!! Form::close() !!}
</div>
</section>
<section class="on_desktop">
<img src="/img/lock.svg">
</section>
@endsection

View file

@ -1,31 +0,0 @@
@extends('layouts.main')
@section('content')
<p class="text-center">
You already have an account?
<a class="ml-2 btn btn-primary btn-sm" href="{{ route('account.login') }}">Login</a>
</p>
<hr />
<h2>Register a new account</h2>
<div class="list-group mb-3 pt-2">
<a href="{{ route('account.register.email') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Using your email address</h5>
</div>
<p class="mb-1">Register on our service with an email address</p>
</a>
@if (config('app.phone_authentication'))
<a href="{{ route('account.register.phone') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">With your phone number</h5>
</div>
<p class="mb-1">Use your phone number to register</p>
</a>
@endif
</div>
@endsection

View file

@ -1,38 +1,52 @@
@extends('layouts.main')
@extends('layouts.main', ['welcome' => true])
@section('content')
<h2>Register using an email address</h2>
<section>
{!! Form::open(['route' => 'account.store.email']) !!}
<p class="oppose">
You already have an account?
<a class="btn btn-secondary" href="{{ route('account.login') }}">Login</a>
</p>
<p>Fill a username and an email address, you will then be able to set a password to finish the registration process.</p>
<h1><i class="material-icons">account_circle</i> Register</h1>
<div class="form-group">
{!! Form::label('username', 'SIP Username') !!}
<div class=" input-group">
{!! Form::text('username', old('username'), ['class' => 'form-control', 'placeholder' => 'username', 'required']) !!}
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">{{ $domain }}</span>
</div>
</div>
<small class="form-text text-muted mb-3">Shoudn't be a phone number. Capital letters are not allowed.</small>
@include('parts.tabs.register')
{!! Form::open(['route' => 'account.store']) !!}
<div>
{!! Form::text('username', old('username'), ['placeholder' => 'username', 'required']) !!}
{!! Form::label('username', 'Username') !!}
@include('parts.errors', ['name' => 'username'])
</div>
<hr />
<div class="form-row">
<div class="form-group col-md-6">
{!! Form::label('email', 'Email') !!}
{!! Form::email('email', old('email'), ['class' => 'form-control', 'placeholder' => 'bob@example.net']) !!}
</div>
<div class="form-group col-md-6">
{!! Form::label('email_confirmation', 'Email confirmation') !!}
{!! Form::email('email_confirmation', old('email_confirm'), ['class' => 'form-control', 'placeholder' => 'bob@example.net']) !!}
</div>
<div>
<input type="text" name="username" value="{{ $domain }}" disabled>
</div>
<div>
{!! Form::email('email', old('email'), ['placeholder' => 'bob@example.net', 'required']) !!}
{!! Form::label('email', 'Email') !!}
@include('parts.errors', ['name' => 'email'])
</div>
<div>
{!! Form::email('email_confirmation', old('email_confirm'), ['placeholder' => 'bob@example.net', 'required']) !!}
{!! Form::label('email_confirmation', 'Confirm email') !!}
</div>
<div>
{!! Form::password('password', ['required']) !!}
{!! Form::label('password', 'Password') !!}
@include('parts.errors', ['name' => 'password'])
</div>
<div>
{!! Form::password('password_confirmation', ['required']) !!}
{!! Form::label('password_confirmation', 'Confirm password') !!}
</div>
@if (!empty(config('app.newsletter_registration_address')))
<div class="form-check mb-3">
<div class="large checkbox">
{!! Form::checkbox('newsletter', 'true', false, ['class' => 'form-check-input', 'id' => 'newsletter']) !!}
<label class="form-check-label" for="newsletter">I would like to subscribe to the newsletter</a></label>
</div>
@ -40,7 +54,19 @@
@include('parts.terms')
{!! Form::submit('Register', ['class' => 'btn btn-primary btn-centered']) !!}
<div class="large">
{!! Form::submit('Register', ['class' => 'btn oppose']) !!}
</div>
{!! Form::close() !!}
</section>
<section class="on_desktop">
<img src="/img/login.svg">
</section>
@endsection
@section('footer')
Hop
@endsection

View file

@ -1,32 +1,56 @@
@extends('layouts.main')
@extends('layouts.main', ['welcome' => true])
@section('content')
<h2>Register using a phone number</h2>
<section>
{!! Form::open(['route' => 'account.store.phone']) !!}
<p class="oppose">
You already have an account?
<a class="btn btn-secondary" href="{{ route('account.login') }}">Login</a>
</p>
<p>Please enter your phone number and optionally a username. When the username is set, it will identify you on the service: other users won't need to know your phone number to call you.<br />The next step of the registration procedure will ask you to setup a password.</p>
<h1><i class="material-icons">account_circle</i> Register</h1>
@include('parts.tabs.register')
<div class="form-group">
{!! Form::open(['route' => 'account.store']) !!}
<div>
{!! Form::text('username', old('username'), ['placeholder' => 'username', 'required']) !!}
{!! Form::label('username', 'Username') !!}
@include('parts.errors', ['name' => 'username'])
</div>
<div>
{!! Form::text('username', $domain, ['disabled']) !!}
</div>
<div>
{!! Form::password('password', ['required']) !!}
{!! Form::label('password', 'Password') !!}
@include('parts.errors', ['name' => 'password'])
</div>
<div>
{!! Form::password('password_confirmation', ['required']) !!}
{!! Form::label('password_confirmation', 'Confirm password') !!}
</div>
<div clas="large">
{!! Form::text('phone', old('phone'), ['placeholder' => '+123456789', 'required']) !!}
{!! Form::label('phone', 'Phone number') !!}
{!! Form::text('phone', old('phone'), ['class' => 'form-control', 'placeholder' => '+123456789']) !!}
@include('parts.errors', ['name' => 'phone'])
</div>
<div class="form-group">
{!! Form::label('username', 'SIP Username (optional)') !!}
<div class=" input-group">
{!! Form::text('username', old('username'), ['class' => 'form-control', 'placeholder' => 'username']) !!}
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">{{ $domain }}</span>
</div>
</div>
<small class="form-text text-muted mb-3">Shoudn't be a phone number. Capital letters are not allowed.</small>
</div>
@include('parts.terms')
{!! Form::submit('Register', ['class' => 'btn btn-primary btn-centered']) !!}
<div class="large">
{!! Form::submit('Register', ['class' => 'btn oppose']) !!}
</div>
{!! Form::close() !!}
</section>
<section class="on_desktop">
<img src="/img/login.svg">
</section>
@endsection

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">
@ -21,9 +21,9 @@
@section('content')
@if ($account->id)
<h2>Edit an account</h2>
<h1>Edit an account</h1>
@else
<h2>Create an account</h2>
<h1>Create an account</h1>
@endif
{!! Form::model($account, [
@ -34,65 +34,53 @@
? 'put'
: 'post'
]) !!}
<div class="form-row">
<div class="form-group col-md-12">
{!! Form::label('username', 'Username') !!}
<div class="input-group">
{!! Form::text('username', $account->username, ['class' => 'form-control', 'placeholder' => 'Username', 'required' => 'required']); !!}
@if (config('app.admins_manage_multi_domains'))
<div class="input-group-append">
<span class="input-group-text" id="basic-addon1">@</span>
</div>
{!! Form::text('domain', $account->domain ?? config('app.sip_domain'), ['class' => 'form-control', 'placeholder' => 'domain.com', 'required' => 'required']); !!}
@else
<div class="input-group-append">
<span class="input-group-text" id="basic-addon1">@ {{ config('app.sip_domain') }}</span>
</div>
@endif
</div>
</div>
<div>
{!! Form::text('username', $account->username, ['placeholder' => 'Username', 'required' => 'required']); !!}
{!! Form::label('username', 'Username') !!}
</div>
<div>
@if (config('app.admins_manage_multi_domains'))
{!! Form::text('domain', $account->domain ?? config('app.sip_domain'), ['placeholder' => 'domain.com', 'required' => 'required']); !!}
@else
{!! Form::text('domain', $account->domain ?? config('app.sip_domain'), ['placeholder' => 'domain.com', 'disabled']); !!}
@endif
{!! Form::label('domain', 'Domain') !!}
</div>
<div class="form-row">
<div class="form-group col-md-6">
{!! Form::label('password', ($account->id) ? 'Password (fill to change)' : 'Password') !!}
{!! Form::password('password', ['class' => 'form-control', 'placeholder' => 'Password']); !!}
<div class="form-check mt-3">
{!! Form::checkbox('password_sha256', 'checked', $account->sha256Password, ['class' => 'form-check-input']) !!}
{!! Form::label('password_sha256', 'Use a SHA-256 encrypted password', ['class' => 'form-check-label']) !!}
</div>
</div>
<div>
{!! Form::password('password', ['placeholder' => 'Password', 'required']); !!}
{!! Form::label('password', ($account->id) ? 'Password (fill to change)' : 'Password') !!}
</div>
<div>
{!! Form::checkbox('password_sha256', 'checked', $account->sha256Password) !!}
{!! Form::label('password_sha256', 'Use a SHA-256 encrypted password') !!}
</div>
<div>
{!! Form::email('email', $account->email, ['placeholder' => 'Email']); !!}
{!! Form::label('email', 'Email') !!}
</div>
<div>
{!! Form::text('display_name', $account->display_name, ['placeholder' => 'John Doe']); !!}
{!! Form::label('display_name', 'Display Name') !!}
</div>
<div>
{!! Form::text('phone', $account->phone, ['placeholder' => '+12123123']); !!}
{!! Form::label('phone', 'Phone') !!}
</div>
<div class="select">
{!! Form::select('dtmf_protocol', $protocols, $account->dtmf_protocol); !!}
{!! Form::label('dtmf_protocol', 'DTMF Protocol') !!}
</div>
<hr />
<div class="form-row">
<div class="form-group col-md-12 mb-0">
<h4>Optional</h4>
</div>
<div class="form-group col-md-6">
{!! Form::label('email', 'Email') !!}
{!! Form::email('email', $account->email, ['class' => 'form-control', 'placeholder' => 'Email']); !!}
</div>
<div class="form-group col-md-6">
{!! Form::label('display_name', 'Display Name') !!}
{!! Form::text('display_name', $account->display_name, ['class' => 'form-control', 'placeholder' => 'John Doe']); !!}
</div>
<div class="form-group col-md-6">
{!! Form::label('phone', 'Phone') !!}
{!! Form::text('phone', $account->phone, ['class' => 'form-control', 'placeholder' => '+12123123']); !!}
</div>
<div class="form-group col-md-6">
{!! Form::label('dtmf_protocol', 'DTMF Protocol') !!}
{!! Form::select('dtmf_protocol', $protocols, $account->dtmf_protocol, ['class' => 'form-control']); !!}
</div>
<div>
{!! Form::submit(($account->id) ? 'Update' : 'Create', ['class' => 'btn oppose']) !!}
</div>
{!! Form::submit(($account->id) ? 'Update' : 'Create', ['class' => 'btn btn-success btn-centered']) !!}
{!! Form::close() !!}
@endsection

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item active" aria-current="page">Accounts</li>
@ -6,64 +6,66 @@
@section('content')
<div class="row mb-2">
<div class="col-sm">
<a class="btn btn-success float-right" href="{{ route('admin.account.create') }}">Create</a>
<h2>Accounts</h2>
<div>
<a class="btn oppose" href="{{ route('admin.account.create') }}">
<i class="material-icons">add_circle</i>
Create
</a>
<h1><i class="material-icons">people</i> Account</h1>
</div>
<div class="col-sm">
<div>
{!! Form::open(['route' => 'admin.account.search']) !!}
<div class="form-row">
<div class="col-8">
{!! Form::text('search', $search, ['class' => 'form-control', 'placeholder' => 'Search by username: +1234, foo_bar…']) !!}
</div>
<div class="col-4">
<button type="submit" class="btn btn-primary">Search</button>
</div>
<div>
{!! Form::text('search', $search, ['placeholder' => 'Search by username: +1234, foo_bar…']) !!}
{!! Form::label('search', 'Search') !!}
</div>
<div>
<button type="submit" class="btn oppose">Search</button>
</div>
{!! Form::close() !!}
</div>
</div>
<table class="table table-responsive-md">
<thead>
<tr>
<th scope="col">Identifier (email)</th>
<th scope="col"></th>
<th scope="col">Created</th>
</tr>
</thead>
<tbody>
@foreach ($accounts as $account)
<br />
<table class="table table-responsive-md">
<thead>
<tr>
<td>
<a href="{{ route('admin.account.show', $account->id) }}">
{{ $account->identifier }}
</a>
</td>
<td>
@if ($account->externalAccount)
<span class="badge badge-secondary" title="External Account attached">EA</span>
@endif
@if ($account->email)
<span class="badge badge-info">Email</span>
@endif
@if ($account->activated)
<span class="badge badge-success" title="Activated">Act.</span>
@endif
@if ($account->admin)
<span class="badge badge-primary" title="Admin">Adm.</span>
@endif
@if ($account->sha256Password)
<span class="badge badge-info">SHA256</span>
@endif
</td>
<td>{{ $account->creation_time}}</td>
<th scope="col">Identifier (email)</th>
<th scope="col"></th>
<th scope="col">Created</th>
</tr>
@endforeach
</tbody>
</table>
</thead>
<tbody>
@foreach ($accounts as $account)
<tr>
<td>
<a href="{{ route('admin.account.show', $account->id) }}">
{{ $account->identifier }}
</a>
</td>
<td>
@if ($account->externalAccount)
<span class="badge badge-secondary" title="External Account attached">EA</span>
@endif
@if ($account->email)
<span class="badge badge-info">Email</span>
@endif
@if ($account->activated)
<span class="badge badge-success" title="Activated">Act.</span>
@endif
@if ($account->admin)
<span class="badge badge-primary" title="Admin">Adm.</span>
@endif
@if ($account->sha256Password)
<span class="badge badge-info">SHA256</span>
@endif
</td>
<td>{{ $account->creation_time}}</td>
</tr>
@endforeach
</tbody>
</table>
{{ $accounts->links('pagination::bootstrap-4') }}
{{ $accounts->links('pagination::bootstrap-4') }}
@endsection

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">
@ -11,10 +11,10 @@
@section('content')
<a class="btn btn-danger float-right" href="{{ route('admin.account.delete', $account->id) }}">Delete</a>
<a class="btn float-right mr-2" href="{{ route('admin.account.edit', $account->id) }}">Edit</a>
<a class="btn oppose" href="{{ route('admin.account.delete', $account->id) }}">Delete</a>
<a class="btn oppose" href="{{ route('admin.account.edit', $account->id) }}">Edit</a>
<h2>Account</h2>
<h1>Account</h1>
<p>
<b>Id:</b> {{ $account->id }}<br />

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item active" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item active" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.account')
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item active" aria-current="page">

View file

@ -1,4 +1,4 @@
@extends('layouts.main', ['large' => true])
@extends('layouts.main')
@section('content')
{{-- This view is only a wrapper for the markdown page --}}

View file

@ -1,20 +1,11 @@
@extends('layouts.base')
@section('header')
<nav class="navbar navbar-expand">
<div class="collapse navbar-collapse" >
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="{{ route('account.panel') }}">{{ config('app.name') }}</a>
</li>
</ul>
@if (Auth::check())
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="{{ route('account.logout') }}">Logout</a>
</li>
</ul>
@endif
<nav>
<a href="{{ route('account.dashboard') }}">{{ config('app.name') }}</a>
@if (Auth::check())
<a href="{{ route('account.logout') }}">Logout</a>
@endif
</div>
</nav>
@endsection
@ -25,4 +16,4 @@
@include('parts.breadcrumb')
@yield('content')
</div>
@endsection
@endsection

View file

@ -1,32 +1,38 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name') }}</title>
<link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
@if (config('instance.custom_theme'))
@if (file_exists(public_path('css/'.config('app.env').'.style.css')))
<link rel="stylesheet" type="text/css" href="{{ asset('css/'.config('app.env').'.style.css') }}" >
@else
<link rel="stylesheet" type="text/css" href="{{ asset('css/style.css') }}" >
@endif
<link rel="stylesheet" type="text/css" href="{{ asset('css/charts.css') }}" >
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name') }}</title>
<!--<link href="{{ asset('css/bootstrap.min.css') }}" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">-->
@if (config('instance.custom_theme'))
@if (file_exists(public_path('css/' . config('app.env') . '.style.css')))
<link rel="stylesheet" type="text/css" href="{{ asset('css/' . config('app.env') . '.style.css') }}">
@else
<link rel="stylesheet" type="text/css" href="{{ asset('css/far.css') }}">
@endif
</head>
<body>
<header>
@yield('header')
</header>
@yield('body')
<footer class="text-center mt-2">
@if (config('instance.copyright'))
{{ config('instance.copyright') }} |
@endif
<a href="{{ route('account.documentation') }}">Documentation</a> |
<a href="{{ route('api') }}">API</a>
</footer>
</body>
<!--<link rel="stylesheet" type="text/css" href="{{ asset('css/charts.css') }}" >-->
@endif
</head>
<body class="@yield('classes')">
<header>
@yield('header')
</header>
@yield('body')
<!--
<footer class="text-center mt-2">
@if (config('instance.copyright'))
{{ config('instance.copyright') }} |
@endif
<a href="{{ route('account.documentation') }}">Documentation</a> |
<a href="{{ route('api') }}">API</a>
</footer>-->
</body>
</html>

View file

@ -1,40 +1,61 @@
@extends('layouts.base')
<!DOCTYPE html>
@section('header')
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
@if (config('app.web_panel'))
<nav class="navbar navbar-expand-lg">
<div class="collapse navbar-collapse" >
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="{{ route('account.home') }}">{{ config('app.name') }}</a>
</li>
@if (isset($user) && get_class($user) == 'App\Account')
<li class="nav-item active">
<a class="nav-link" href="{{ route('account.panel') }}">My Account</a>
</li>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name') }}</title>
@if (config('instance.custom_theme'))
@if (file_exists(public_path('css/' . config('app.env') . '.style.css')))
<link rel="stylesheet" type="text/css" href="{{ asset('css/' . config('app.env') . '.style.css') }}">
@else
<link rel="stylesheet" type="text/css" href="{{ asset('css/far.css') }}">
@endif
<!--<link rel="stylesheet" type="text/css" href="{{ asset('css/charts.css') }}" >-->
@endif
</head>
<body class="@if (isset($welcome) && $welcome) welcome @endif">
<header>
@if (config('app.web_panel'))
<nav>
<a href="{{ route('account.home') }}">{{ config('app.name') }}</a>
@if (auth()->user())
<a href="{{ route('account.dashboard') }}">
<i class="material-icons">person</i>My Account
</a>
<a href="{{ route('account.logout') }}">
<i class="material-icons">logout</i>
</a>
@else
<a href="{{ route('account.login') }}">
<i class="material-icons">info</i> Login
</a>
@endif
</ul>
</nav>
@endif
</header>
<ul class="navbar-nav">
@if (publicRegistrationEnabled())
<li class="nav-item @if (request()->routeIs('account.register')) active @endif">
<a class="nav-link" href="{{ route('account.register') }}">Register</a>
</li>
@endif
<li class="nav-item @if (request()->routeIs('account.login')) active @endif">
<a class="nav-link" href="{{ route('account.login') }}">Login</a>
</li>
</ul>
</div>
</nav>
@endif
<content>
@if (!isset($welcome) || $welcome == false)
@include('parts.sidebar')
@endif
@endsection
@section('body')
<div class="container @if (isset($large) && $large) large @endif pt-4">
@include('parts.errors')
@if (!isset($welcome) || $welcome == false)
<section>
@endif
@yield('content')
</div>
@endsection
@if (!isset($welcome) || $welcome == false)
</section>
@endif
</content>
</body>
</html>

View file

@ -6,10 +6,10 @@
<p>Hello,</p>
<p>
You are trying to authenticate to {{ config('app.name') }} using your email account.<br />
Please follow the unique link bellow to finish the authentication process.
Please enter the code bellow to finish the authentication process.
</p>
<p>
<a href="{{ $link }}">{{ $link }}</a>
<h2>{{ $recovery_code }}</h2>
</p>
<p>
You can as well configure your new device using the following code or by directly flashing the QRCode:<br />

View file

@ -6,10 +6,10 @@
<p>Hello,</p>
<p>
You are trying to authenticate to {{ config('app.name') }} using your email account.<br />
Please follow the unique link bellow to finish the authentication process.
Please enter the code bellow to finish the authentication process.
</p>
<p>
<a href="{{ $link }}">{{ $link }}</a>
<h2>{{ $recovery_code }}</h2>
</p>
<p>
You can as well configure your new device using the following code or by directly flashing the QRCode:<br />

View file

@ -1,9 +1,9 @@
Hello,
You are trying to authenticate to {{ config('app.name') }} using your email account.
Please follow the unique link bellow to finish the authentication process.
Please enter the code bellow to finish the authentication process.
{{ $link }}
{{ $recovery_code }}
You can as well configure your new device using the following code or by directly flashing the QRCode in the following link:

View file

@ -1,9 +1,9 @@
Hello,
You are trying to authenticate to {{ config('app.name') }} using your email account.
Please follow the unique link bellow to finish the authentication process.
Please enter the code bellow to finish the authentication process.
{{ $link }}
{{ $recovery_code }}
You can as well configure your new device using the following code or by directly flashing the QRCode in the following link:

View file

@ -1,15 +0,0 @@
<html>
<head>
<title>Changing your email address</title>
</head>
<body>
<p>Hello,</p>
<p>
You have changed your email address to the current one on {{ config('app.name') }}.
</p>
<p>
Regards,<br />
{{ config('mail.signature') }}
</p>
</body>
</html>

View file

@ -1,6 +0,0 @@
Hello,
You have changed your email address to the current one on {{ config('app.name') }}.
Regards,
{{ config('mail.signature') }}

View file

@ -1,24 +0,0 @@
<html>
<head>
<title>Changing your email address</title>
</head>
<body>
<p>Hello,</p>
<p>
You requested to change your email address to {{ $account->emailChanged->new_email }} on {{ config('app.name') }}.
</p>
<p>
To confirm this change please click on the following link:
<a href="{{ route('account.email.update', ['hash' => $account->emailChanged->hash]) }}">
{{ route('account.email.update', ['hash' => $account->emailChanged->hash]) }}
</a>.
</p>
<p>
If you are not at the origin of this change just ignore this message.
</p>
<p>
Regards,<br />
{{ config('mail.signature') }}
</p>
</body>
</html>

View file

@ -1,10 +0,0 @@
Hello,
You requested to change your email address from {{ $account->emailChanged->new_email }} on {{ config('app.name') }}.
To confirm this change please click on the following link: {{ route('account.email.update', ['hash' => $account->emailChanged->hash]) }}.
If you are not at the origin of this change just ignore this message.
Regards,
{{ config('mail.signature') }}

View file

@ -0,0 +1,19 @@
<html>
<head>
<title>Account registered on {{ config('app.name') }}</title>
</head>
<body>
<p>Hello,</p>
<p>
You just created an account on {{ config('app.name') }} using your email account.<br />
Please enter the following code on the confirmation page:
</p>
<p>
<h2>{{ $code }}</h2>
</p>
<p>
Regards,<br />
{{ config('mail.signature') }}
</p>
</body>
</html>

View file

@ -0,0 +1,10 @@
Hello,
You just created an account on {{ config('app.name') }} using your email account.
Please enter the following code on the confirmation page:
{{ $code }}
Regards,
{{ config('mail.signature') }}

View file

@ -1,7 +1,7 @@
@hasSection('breadcrumb')
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ route('account.panel') }}">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('account.dashboard') }}">Home</a></li>
@yield('breadcrumb')
</ol>
</nav>

View file

@ -1,4 +1,6 @@
<div class="form-group">
<div class="large">
{!! NoCaptcha::renderJs() !!}
{!! NoCaptcha::display() !!}
@include('parts.errors', ['name' => 'g-recaptcha-response'])
</div>

View file

@ -1,22 +1,7 @@
@if (isset($errors) && $errors->any())
<div class="alert alert-danger">
<ul class="mb-0 pl-2">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@if (Session::has('error'))
<div class="alert alert-danger">
{{Session::get('error')}}
</div>
@endif
@if (Session::has('success'))
<div class="alert alert-success">
{{Session::get('success')}}
</div>
@if (isset($errors) && isset($name) && count($errors->get($name)) > 0)
@foreach ($errors->get($name) as $error)
<small>
{{ $error }}
</small>
@endforeach
@endif

View file

@ -0,0 +1,22 @@
@if (isset($errors) && $errors->any())
<div class="alert alert-danger">
<ul class="mb-0 pl-2">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@if (Session::has('error'))
<div class="alert alert-danger">
{{Session::get('error')}}
</div>
@endif
@if (Session::has('success'))
<div class="alert alert-success">
{{Session::get('success')}}
</div>
@endif

View file

@ -5,9 +5,9 @@
@else
Set or recover your password
@endif
using your <a href="{{ route('account.login_email') }}">Email address</a>
using your <a href="{{ route('account.recovery.show.email') }}">Email address</a>
@if (config('app.phone_authentication'))
or your <a href="{{ route('account.login_phone') }}">Phone number</a>
or your <a href="{{ route('account.recovery.show.phone') }}">Phone number</a>
@endif
</p>
<p class="text-center">

View file

@ -0,0 +1,12 @@
<nav>
@foreach ([
'account.dashboard' => ['title' => 'Dashboard', 'icon' => 'dashboard'],
'admin.account.index' => ['title' => 'Accounts', 'icon' => 'people'],
'admin.statistics.show.day' => ['title' => 'Statistics', 'icon' => 'analytics'],
] as $route => $value)
<a @if (str_starts_with(url()->current(), route($route)))class="current"@endif href="{{ route($route) }}">
<i class="material-icons">{{ $value['icon'] }}</i>
{{ $value['title'] }}
</a>
@endforeach
</nav>

View file

@ -0,0 +1,5 @@
<ul class="tabs">
@foreach ($items as $route => $title)
<li @if (url()->current() == route($route))class="current"@endif><a href="{{ route($route) }}">{{ $title }}</a></li>
@endforeach
</ul>

View file

@ -0,0 +1,6 @@
@if(config('app.phone_authentication'))
@include('parts.tabs', ['items' => [
'account.register.email' => 'Email registration',
'account.register.phone' => 'Phone registration',
]])
@endif

View file

@ -1,13 +1,17 @@
<div class="form-check mb-3">
{!! Form::checkbox('terms', 'true', false, ['class' => 'form-check-input', 'id' => 'terms']) !!}
<label class="form-check-label" for="terms">I accept the Terms and Conditions: </a></label>
<p>Read the <a href="{{ config('app.terms_of_use_url') }}">Terms and Conditions</a></p>
<div class="large checkbox">
{!! Form::checkbox('terms', 'true', false, ['id' => 'terms']) !!}
<label for="terms">I accept the Terms and Conditions: </a>
Read the <a href="{{ config('app.terms_of_use_url') }}">Terms and Conditions</a>
</label>
@include('parts.errors', ['name' => 'terms'])
</div>
<div class="form-check mb-3">
{!! Form::checkbox('privacy', 'true', false, ['class' => 'form-check-input', 'id' => 'privacy']) !!}
<label class="form-check-label" for="privacy">I accept the Privacy policy: </a></label>
<p>Read the <a href="{{ config('app.privacy_policy_url') }}">Privacy policy</a></p>
<div class="large checkbox">
{!! Form::checkbox('privacy', 'true', false, ['id' => 'privacy']) !!}
<label for="privacy">I accept the Privacy policy: </a>
Read the <a href="{{ config('app.privacy_policy_url') }}">Privacy policy</a>
</label>
@include('parts.errors', ['name' => 'privacy'])
</div>
@include('parts.captcha')
@include('parts.captcha')

View file

@ -17,27 +17,30 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
Route::get('/', 'Account\AccountController@home')->name('account.home');
use App\Http\Controllers\Account\AccountController;
use App\Http\Controllers\Account\CreationRequestTokenController;
use App\Http\Controllers\Account\DeviceController;
use App\Http\Controllers\Account\EmailController;
use App\Http\Controllers\Account\PasswordController;
use App\Http\Controllers\Account\PhoneController;
use App\Http\Controllers\Account\ProvisioningController;
use App\Http\Controllers\Account\RecoveryController;
use Illuminate\Support\Facades\Route;
Route::redirect('/', '/login')->name('account.home');
Route::get('documentation', 'Account\AccountController@documentation')->name('account.documentation');
if (config('app.web_panel')) {
Route::get('login', 'Account\AuthenticateController@login')->name('account.login');
Route::post('authenticate', 'Account\AuthenticateController@authenticate')->name('account.authenticate');
Route::get('login/email', 'Account\AuthenticateController@loginEmail')->name('account.login_email');
Route::post('authenticate/email', 'Account\AuthenticateController@authenticateEmail')->name('account.authenticate.email');
Route::get('authenticate/email/check/{sip}', 'Account\AuthenticateController@checkEmail')->name('account.check.email');
Route::get('authenticate/email/{code}', 'Account\AuthenticateController@validateEmail')->name('account.authenticate.email_confirm');
Route::get('login/phone', 'Account\AuthenticateController@loginPhone')->name('account.login_phone');
Route::post('authenticate/phone', 'Account\AuthenticateController@authenticatePhone')->name('account.authenticate.phone');
Route::post('authenticate/phone/confirm', 'Account\AuthenticateController@validatePhone')->name('account.authenticate.phone_confirm');
Route::get('authenticate/qrcode/{token?}', 'Account\AuthenticateController@loginAuthToken')->name('account.authenticate.auth_token');
}
Route::get('creation_token/check/{token}', 'Account\CreationRequestTokenController@check')->name('account.creation_request_token.check');
Route::post('creation_token/validate', 'Account\CreationRequestTokenController@validateToken')->name('account.creation_request_token.validate');
Route::prefix('creation_token')->controller(CreationRequestTokenController::class)->group(function () {
Route::get('check/{token}', 'check')->name('account.creation_request_token.check');
Route::post('validate', 'validateToken')->name('account.creation_request_token.validate');
});
Route::group(['middleware' => 'auth.digest_or_key'], function () {
Route::get('provisioning/me', 'Account\ProvisioningController@me')->name('provisioning.me');
@ -47,40 +50,69 @@ Route::group(['middleware' => 'auth.digest_or_key'], function () {
Route::get('contacts/vcard', 'Account\ContactVcardController@index')->name('account.contacts.vcard.index');
});
Route::get('provisioning/auth_token/{auth_token}', 'Account\ProvisioningController@authToken')->name('provisioning.auth_token');
Route::get('provisioning/qrcode/{provisioning_token}', 'Account\ProvisioningController@qrcode')->name('provisioning.qrcode');
Route::get('provisioning/{provisioning_token?}', 'Account\ProvisioningController@show')->name('provisioning.show');
Route::prefix('provisioning')->controller(ProvisioningController::class)->group(function () {
Route::get('auth_token/{auth_token}', 'authToken')->name('provisioning.auth_token');
Route::get('qrcode/{provisioning_token}', 'qrcode')->name('provisioning.qrcode');
Route::get('{provisioning_token?}', 'show')->name('provisioning.show');
});
if (publicRegistrationEnabled()) {
Route::redirect('register', 'register/email')->name('account.register');
if (config('app.phone_authentication')) {
Route::get('register/phone', 'Account\RegisterController@registerPhone')->name('account.register.phone');
Route::post('register/phone', 'Account\RegisterController@storePhone')->name('account.store.phone');
}
Route::get('register', 'Account\RegisterController@register')->name('account.register');
Route::get('register/email', 'Account\RegisterController@registerEmail')->name('account.register.email');
Route::post('register/email', 'Account\RegisterController@storeEmail')->name('account.store.email');
Route::post('accounts', 'Account\AccountController@store')->name('account.store');
}
if (config('app.web_panel')) {
Route::group(['middleware' => 'auth'], function () {
Route::get('panel', 'Account\AccountController@panel')->name('account.panel');
Route::prefix('recover')->controller(RecoveryController::class)->group(function () {
Route::get('phone', 'showPhone')->name('account.recovery.show.phone');
Route::get('email', 'showEmail')->name('account.recovery.show.email');
Route::post('/', 'send')->name('account.recovery.send');
Route::post('/confirm', 'confirm')->name('account.recovery.confirm');
});
Route::middleware(['auth'])->group(function () {
// Email change and validation
Route::prefix('recover')->controller(EmailController::class)->group(function () {
Route::get('change', 'change')->name('account.email.change');
Route::post('change', 'requestChange')->name('account.email.request_change');
Route::get('validate', 'validateChange')->name('account.email.validate');
Route::post('/', 'store')->name('account.email.update');
});
// Phone change and validation
Route::prefix('phone')->controller(PhoneController::class)->group(function () {
Route::get('change', 'change')->name('account.phone.change');
Route::post('change', 'requestChange')->name('account.phone.request_change');
Route::get('validate', 'validateChange')->name('account.phone.validate');
Route::post('/', 'store')->name('account.phone.update');
});
Route::controller(AccountController::class)->group(function () {
Route::get('dashboard', 'panel')->name('account.dashboard');
Route::post('api_key', 'generateApiKey')->name('account.api_key.generate');
Route::get('delete', 'delete')->name('account.delete');
Route::delete('delete', 'destroy')->name('account.destroy');
});
Route::get('logout', 'Account\AuthenticateController@logout')->name('account.logout');
Route::post('api_key', 'Account\AccountController@generateApiKey')->name('account.api_key.generate');
Route::prefix('password')->controller(PasswordController::class)->group(function () {
Route::get('/', 'show')->name('account.password');
Route::post('/', 'update')->name('account.password.update');
});
Route::get('delete', 'Account\AccountController@delete')->name('account.delete');
Route::delete('delete', 'Account\AccountController@destroy')->name('account.destroy');
Route::get('email', 'Account\EmailController@show')->name('account.email');
Route::post('email/request', 'Account\EmailController@requestUpdate')->name('account.email.request_update');
Route::get('email/{hash}', 'Account\EmailController@update')->name('account.email.update');
Route::get('password', 'Account\PasswordController@show')->name('account.password');
Route::post('password', 'Account\PasswordController@update')->name('account.password.update');
Route::get('devices', 'Account\DeviceController@index')->name('account.device.index');
Route::get('devices/delete/{id}', 'Account\DeviceController@delete')->name('account.device.delete');
Route::delete('devices', 'Account\DeviceController@destroy')->name('account.device.destroy');
Route::prefix('devices')->controller(DeviceController::class)->group(function () {
Route::get('/', 'index')->name('account.device.index');
Route::get('delete/{id}', 'delete')->name('account.device.delete');
Route::delete('/', 'destroy')->name('account.device.destroy');
});
Route::post('auth_tokens', 'Account\AuthTokenController@create')->name('account.auth_tokens.create');
@ -90,62 +122,64 @@ if (config('app.web_panel')) {
Route::get('auth_tokens/qrcode/{token}', 'Account\AuthTokenController@qrcode')->name('auth_tokens.qrcode');
Route::get('auth_tokens/auth/{token}', 'Account\AuthTokenController@auth')->name('auth_tokens.auth');
Route::group(['middleware' => 'auth.admin'], function () {
Route::prefix('admin')->middleware(['auth.admin'])->group(function () {
// Statistics
Route::get('admin/statistics/day', 'Admin\StatisticsController@showDay')->name('admin.statistics.show.day');
Route::get('admin/statistics/week', 'Admin\StatisticsController@showWeek')->name('admin.statistics.show.week');
Route::get('admin/statistics/month', 'Admin\StatisticsController@showMonth')->name('admin.statistics.show.month');
Route::get('statistics/day', 'Admin\StatisticsController@showDay')->name('admin.statistics.show.day');
Route::get('statistics/week', 'Admin\StatisticsController@showWeek')->name('admin.statistics.show.week');
Route::get('statistics/month', 'Admin\StatisticsController@showMonth')->name('admin.statistics.show.month');
// Account types
Route::get('admin/accounts/types', 'Admin\AccountTypeController@index')->name('admin.account.type.index');
Route::get('admin/accounts/types/create', 'Admin\AccountTypeController@create')->name('admin.account.type.create');
Route::post('admin/accounts/types', 'Admin\AccountTypeController@store')->name('admin.account.type.store');
Route::get('admin/accounts/types/{type_id}/edit', 'Admin\AccountTypeController@edit')->name('admin.account.type.edit');
Route::put('admin/accounts/types/{type_id}', 'Admin\AccountTypeController@update')->name('admin.account.type.update');
Route::get('admin/accounts/types/{type_id}/delete', 'Admin\AccountTypeController@delete')->name('admin.account.type.delete');
Route::delete('admin/accounts/types/{type_id}', 'Admin\AccountTypeController@destroy')->name('admin.account.type.destroy');
Route::prefix('accounts')->group(function () {
// Account types
Route::get('types', 'Admin\AccountTypeController@index')->name('admin.account.type.index');
Route::get('types/create', 'Admin\AccountTypeController@create')->name('admin.account.type.create');
Route::post('types', 'Admin\AccountTypeController@store')->name('admin.account.type.store');
Route::get('types/{type_id}/edit', 'Admin\AccountTypeController@edit')->name('admin.account.type.edit');
Route::put('types/{type_id}', 'Admin\AccountTypeController@update')->name('admin.account.type.update');
Route::get('types/{type_id}/delete', 'Admin\AccountTypeController@delete')->name('admin.account.type.delete');
Route::delete('types/{type_id}', 'Admin\AccountTypeController@destroy')->name('admin.account.type.destroy');
Route::get('admin/accounts/{account}/types/create', 'Admin\AccountAccountTypeController@create')->name('admin.account.account_type.create');
Route::post('admin/accounts/{account}/types', 'Admin\AccountAccountTypeController@store')->name('admin.account.account_type.store');
Route::delete('admin/accounts/{account}/types/{type_id}', 'Admin\AccountAccountTypeController@destroy')->name('admin.account.account_type.destroy');
Route::get('{account}/types/create', 'Admin\AccountAccountTypeController@create')->name('admin.account.account_type.create');
Route::post('{account}/types', 'Admin\AccountAccountTypeController@store')->name('admin.account.account_type.store');
Route::delete('{account}/types/{type_id}', 'Admin\AccountAccountTypeController@destroy')->name('admin.account.account_type.destroy');
// Contacts
Route::get('admin/accounts/{account}/contacts/create', 'Admin\AccountContactController@create')->name('admin.account.contact.create');
Route::post('admin/accounts/{account}/contacts', 'Admin\AccountContactController@store')->name('admin.account.contact.store');
Route::get('admin/accounts/{account}/contacts/{contact_id}/delete', 'Admin\AccountContactController@delete')->name('admin.account.contact.delete');
Route::delete('admin/accounts/{account}/contacts', 'Admin\AccountContactController@destroy')->name('admin.account.contact.destroy');
// Contacts
Route::get('{account}/contacts/create', 'Admin\AccountContactController@create')->name('admin.account.contact.create');
Route::post('{account}/contacts', 'Admin\AccountContactController@store')->name('admin.account.contact.store');
Route::get('{account}/contacts/{contact_id}/delete', 'Admin\AccountContactController@delete')->name('admin.account.contact.delete');
Route::delete('{account}/contacts', 'Admin\AccountContactController@destroy')->name('admin.account.contact.destroy');
// Accounts
Route::get('admin/accounts/{account}/show', 'Admin\AccountController@show')->name('admin.account.show');
// Accounts
Route::get('{account}/show', 'Admin\AccountController@show')->name('admin.account.show');
Route::get('admin/accounts/{account}/activate', 'Admin\AccountController@activate')->name('admin.account.activate');
Route::get('admin/accounts/{account}/deactivate', 'Admin\AccountController@deactivate')->name('admin.account.deactivate');
Route::get('{account}/activate', 'Admin\AccountController@activate')->name('admin.account.activate');
Route::get('{account}/deactivate', 'Admin\AccountController@deactivate')->name('admin.account.deactivate');
Route::get('admin/accounts/{account}/external_account/attach', 'Admin\AccountController@attachExternalAccount')->name('admin.account.external_account.attach');
Route::get('{account}/external_account/attach', 'Admin\AccountController@attachExternalAccount')->name('admin.account.external_account.attach');
Route::get('admin/accounts/{account}/admin', 'Admin\AccountController@admin')->name('admin.account.admin');
Route::get('admin/accounts/{id}/unadmin', 'Admin\AccountController@unadmin')->name('admin.account.unadmin');
Route::get('{account}/admin', 'Admin\AccountController@admin')->name('admin.account.admin');
Route::get('{id}/unadmin', 'Admin\AccountController@unadmin')->name('admin.account.unadmin');
Route::get('admin/accounts/{account}/provision', 'Admin\AccountController@provision')->name('admin.account.provision');
Route::get('{account}/provision', 'Admin\AccountController@provision')->name('admin.account.provision');
Route::get('admin/accounts/create', 'Admin\AccountController@create')->name('admin.account.create');
Route::post('admin/accounts', 'Admin\AccountController@store')->name('admin.account.store');
Route::get('create', 'Admin\AccountController@create')->name('admin.account.create');
Route::post('accounts', 'Admin\AccountController@store')->name('admin.account.store');
Route::get('admin/accounts/{account}/edit', 'Admin\AccountController@edit')->name('admin.account.edit');
Route::put('admin/accounts/{id}', 'Admin\AccountController@update')->name('admin.account.update');
Route::get('{account}/edit', 'Admin\AccountController@edit')->name('admin.account.edit');
Route::put('{id}', 'Admin\AccountController@update')->name('admin.account.update');
Route::get('admin/accounts/{account}/delete', 'Admin\AccountController@delete')->name('admin.account.delete');
Route::delete('admin/accounts', 'Admin\AccountController@destroy')->name('admin.account.destroy');
Route::get('{account}/delete', 'Admin\AccountController@delete')->name('admin.account.delete');
Route::delete('accounts', 'Admin\AccountController@destroy')->name('admin.account.destroy');
Route::get('admin/accounts/{search?}', 'Admin\AccountController@index')->name('admin.account.index');
Route::post('admin/accounts/search', 'Admin\AccountController@search')->name('admin.account.search');
Route::get('{search?}', 'Admin\AccountController@index')->name('admin.account.index');
Route::post('search', 'Admin\AccountController@search')->name('admin.account.search');
// Account actions
Route::get('admin/accounts/{account}/actions/create', 'Admin\AccountActionController@create')->name('admin.account.action.create');
Route::post('admin/accounts/{account}/actions', 'Admin\AccountActionController@store')->name('admin.account.action.store');
Route::get('admin/accounts/{account}/actions/{action_id}/edit', 'Admin\AccountActionController@edit')->name('admin.account.action.edit');
Route::put('admin/accounts/{account}/actions/{action_id}', 'Admin\AccountActionController@update')->name('admin.account.action.update');
Route::get('admin/accounts/{account}/actions/{action_id}/delete', 'Admin\AccountActionController@delete')->name('admin.account.action.delete');
Route::delete('admin/accounts/{account}/actions/{action_id}', 'Admin\AccountActionController@destroy')->name('admin.account.action.destroy');
// Account actions
Route::get('{account}/actions/create', 'Admin\AccountActionController@create')->name('admin.account.action.create');
Route::post('{account}/actions', 'Admin\AccountActionController@store')->name('admin.account.action.store');
Route::get('{account}/actions/{action_id}/edit', 'Admin\AccountActionController@edit')->name('admin.account.action.edit');
Route::put('{account}/actions/{action_id}', 'Admin\AccountActionController@update')->name('admin.account.action.update');
Route::get('{account}/actions/{action_id}/delete', 'Admin\AccountActionController@delete')->name('admin.account.action.delete');
Route::delete('{account}/actions/{action_id}', 'Admin\AccountActionController@destroy')->name('admin.account.action.destroy');
});
});
}

View file

@ -80,7 +80,7 @@ class ApiAccountCreationTokenTest extends TestCase
$response = $this->json($this->method, $this->accountRoute, [
'username' => 'username',
'algorithm' => 'SHA-256',
'password' => '2',
'password' => '123',
'account_creation_token' => '0123456789abc'
]);
$response->assertStatus(422);
@ -89,7 +89,7 @@ class ApiAccountCreationTokenTest extends TestCase
$response = $this->json($this->method, $this->accountRoute, [
'username' => 'username',
'algorithm' => 'SHA-256',
'password' => '2',
'password' => '123',
'account_creation_token' => $token->token
]);
$response->assertStatus(200);
@ -98,7 +98,7 @@ class ApiAccountCreationTokenTest extends TestCase
$response = $this->json($this->method, $this->accountRoute, [
'username' => 'username2',
'algorithm' => 'SHA-256',
'password' => '2',
'password' => '123',
'account_creation_token' => $token->token
]);
$response->assertStatus(422);
@ -114,7 +114,7 @@ class ApiAccountCreationTokenTest extends TestCase
$response = $this->json($this->method, $this->accountRoute, [
'username' => 'blacklisted',
'algorithm' => 'SHA-256',
'password' => '2',
'password' => '123',
'account_creation_token' => $token->token
]);
$response->assertStatus(422);
@ -124,7 +124,7 @@ class ApiAccountCreationTokenTest extends TestCase
$response = $this->json($this->method, $this->accountRoute, [
'username' => 'username-gnap',
'algorithm' => 'SHA-256',
'password' => '2',
'password' => '123',
'account_creation_token' => $token->token
]);
@ -135,7 +135,7 @@ class ApiAccountCreationTokenTest extends TestCase
$response = $this->json($this->method, $this->accountRoute, [
'username' => 'valid-username',
'algorithm' => 'SHA-256',
'password' => '2',
'password' => '123',
'account_creation_token' => $token->token
]);

View file

@ -920,8 +920,8 @@ class ApiAccountTest extends TestCase
->assertStatus(200)
->assertJson([
'username' => $password->account->username,
'email_changed' => [
'new_email' => $newEmail
'email_change_code' => [
'email' => $newEmail
]
]);