From 93c98ae73f7b06ce0918ac3898cbb2fb5811f8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Jaussoin?= Date: Mon, 2 Dec 2024 13:04:49 +0000 Subject: [PATCH] Fix FLEXIAPI-220 Migrate SIP Domains to Spaces --- .gitlab-ci-files/deploy.yml | 8 - .gitlab-ci-files/package.yml | 4 - .gitlab-ci-files/test.yml | 6 - .gitlab-ci.yml | 3 +- CHANGELOG.md | 5 +- Makefile | 4 +- README.md | 6 +- RELEASE.md | 2 +- flexiapi/.env.example | 3 +- flexiapi/app/Account.php | 6 +- .../Commands/Accounts/CreateAdminAccount.php | 12 +- .../Commands/Accounts/CreateAdminTest.php | 2 +- .../{SipDomains => Spaces}/CreateUpdate.php | 23 +- flexiapi/app/Helpers/Utils.php | 2 +- .../Account/AuthTokenController.php | 4 +- .../Account/AuthenticateController.php | 14 +- .../Account/ProvisioningController.php | 4 +- .../Controllers/Admin/AccountController.php | 25 +- .../Controllers/Admin/SipDomainController.php | 120 ----- .../Controllers/Admin/SpaceController.php | 164 ++++++ .../Api/Admin/AccountController.php | 17 +- .../Api/Admin/SipDomainController.php | 111 ---- .../Controllers/Api/Admin/SpaceController.php | 134 +++++ flexiapi/app/Http/Kernel.php | 4 + .../app/Http/Middleware/IsSpaceExpired.php | 19 + flexiapi/app/Http/Middleware/Space.php | 35 ++ .../Account/Create/Api/AsAdminRequest.php | 9 - .../Requests/Account/Create/Api/Request.php | 5 - .../Http/Requests/Account/Create/Request.php | 6 +- .../Account/Create/Web/AsAdminRequest.php | 5 - .../Requests/Account/Create/Web/Request.php | 5 - .../Account/Update/Api/AsAdminRequest.php | 5 - .../Http/Requests/Account/Update/Request.php | 2 +- .../Account/Update/Web/AsAdminRequest.php | 5 - flexiapi/app/Providers/AppServiceProvider.php | 6 - .../app/Providers/RouteServiceProvider.php | 5 - flexiapi/app/SipDomain.php | 31 -- flexiapi/app/Space.php | 89 ++++ flexiapi/composer.lock | 106 ++-- flexiapi/config/app.php | 2 +- flexiapi/config/auth.php | 9 - .../database/factories/AccountFactory.php | 21 +- ...{SipDomainFactory.php => SpaceFactory.php} | 16 +- ..._06_11_090306_create_sip_domains_table.php | 10 +- ...038_rename_sip_domains_to_spaces_table.php | 38 ++ .../seeds/LiblinphoneTesterAccoutSeeder.php | 8 +- flexiapi/public/css/form.css | 87 +++- flexiapi/public/css/style.css | 59 ++- flexiapi/public/scripts/utils.js | 12 + .../views/account/dashboard.blade.php | 4 +- .../views/admin/account/create_edit.blade.php | 6 +- .../views/admin/account/index.blade.php | 3 + .../admin/sip_domain/create_edit.blade.php | 73 --- .../views/admin/sip_domain/index.blade.php | 43 -- .../views/admin/space/create.blade.php | 46 ++ .../{sip_domain => space}/delete.blade.php | 19 +- .../views/admin/space/edit.blade.php | 57 ++ .../views/admin/space/index.blade.php | 55 ++ .../views/admin/space/parameters.blade.php | 45 ++ .../views/admin/space/show.blade.php | 82 +++ .../views/admin/space/tabs.blade.php | 14 + .../api/documentation_markdown.blade.php | 37 +- flexiapi/resources/views/errors/402.blade.php | 5 + flexiapi/resources/views/errors/503.blade.php | 2 +- .../views/errors/illustrated-layout.blade.php | 486 ------------------ .../resources/views/errors/layout.blade.php | 57 -- .../resources/views/errors/minimal.blade.php | 2 +- .../resources/views/layouts/main.blade.php | 4 +- .../resources/views/parts/errors.blade.php | 1 + .../views/parts/form/toggle.blade.php | 21 +- .../resources/views/parts/sidebar.blade.php | 16 +- flexiapi/routes/api.php | 6 +- flexiapi/routes/web.php | 12 +- flexiapi/storage/debugbar/.gitignore | 2 + .../tests/Feature/AccountProvisioningTest.php | 4 +- .../Feature/ApiAccountCreationTokenTest.php | 4 + flexiapi/tests/Feature/ApiAccountTest.php | 23 +- .../tests/Feature/ApiPhoneCountryTest.php | 2 +- ...{ApiSipDomainTest.php => ApiSpaceTest.php} | 69 ++- .../Feature/ApiSpaceWithMiddlewareTest.php | 72 +++ flexiapi/tests/TestCase.php | 83 +-- .../tests/TestCaseWithSpaceMiddleware.php | 31 ++ flexiapi/tests/TestUtilsTrait.php | 103 ++++ flexisip-account-manager.spec | 2 +- 84 files changed, 1470 insertions(+), 1299 deletions(-) rename flexiapi/app/Console/Commands/{SipDomains => Spaces}/CreateUpdate.php (68%) delete mode 100644 flexiapi/app/Http/Controllers/Admin/SipDomainController.php create mode 100644 flexiapi/app/Http/Controllers/Admin/SpaceController.php delete mode 100644 flexiapi/app/Http/Controllers/Api/Admin/SipDomainController.php create mode 100644 flexiapi/app/Http/Controllers/Api/Admin/SpaceController.php create mode 100644 flexiapi/app/Http/Middleware/IsSpaceExpired.php create mode 100644 flexiapi/app/Http/Middleware/Space.php delete mode 100644 flexiapi/app/SipDomain.php create mode 100644 flexiapi/app/Space.php rename flexiapi/database/factories/{SipDomainFactory.php => SpaceFactory.php} (75%) create mode 100644 flexiapi/database/migrations/2024_11_18_103038_rename_sip_domains_to_spaces_table.php delete mode 100644 flexiapi/resources/views/admin/sip_domain/create_edit.blade.php delete mode 100644 flexiapi/resources/views/admin/sip_domain/index.blade.php create mode 100644 flexiapi/resources/views/admin/space/create.blade.php rename flexiapi/resources/views/admin/{sip_domain => space}/delete.blade.php (50%) create mode 100644 flexiapi/resources/views/admin/space/edit.blade.php create mode 100644 flexiapi/resources/views/admin/space/index.blade.php create mode 100644 flexiapi/resources/views/admin/space/parameters.blade.php create mode 100644 flexiapi/resources/views/admin/space/show.blade.php create mode 100644 flexiapi/resources/views/admin/space/tabs.blade.php create mode 100644 flexiapi/resources/views/errors/402.blade.php delete mode 100644 flexiapi/resources/views/errors/illustrated-layout.blade.php delete mode 100644 flexiapi/resources/views/errors/layout.blade.php create mode 100644 flexiapi/storage/debugbar/.gitignore rename flexiapi/tests/Feature/{ApiSipDomainTest.php => ApiSpaceTest.php} (62%) create mode 100644 flexiapi/tests/Feature/ApiSpaceWithMiddlewareTest.php create mode 100644 flexiapi/tests/TestCaseWithSpaceMiddleware.php create mode 100644 flexiapi/tests/TestUtilsTrait.php diff --git a/.gitlab-ci-files/deploy.yml b/.gitlab-ci-files/deploy.yml index be5e3a2..27e2814 100644 --- a/.gitlab-ci-files/deploy.yml +++ b/.gitlab-ci-files/deploy.yml @@ -14,14 +14,6 @@ rocky9-deploy: - rocky9-package - rocky9-test -debian11-deploy: - extends: .deploy - script: - - ./deploy_packages.sh debian bullseye - needs: - - debian11-package - - debian11-test - debian12-deploy: extends: .deploy script: diff --git a/.gitlab-ci-files/package.yml b/.gitlab-ci-files/package.yml index 25e6fb2..0d8f2f7 100644 --- a/.gitlab-ci-files/package.yml +++ b/.gitlab-ci-files/package.yml @@ -22,10 +22,6 @@ rocky9-package: - dnf -y install php-sodium - make rpm-el9 -debian11-package: - extends: .debian_package - image: gitlab.linphone.org:4567/bc/public/docker/debian11-php:$DEBIAN_11_IMAGE_VERSION - debian12-package: extends: .debian_package image: gitlab.linphone.org:4567/bc/public/docker/debian12-php:$DEBIAN_12_IMAGE_VERSION diff --git a/.gitlab-ci-files/test.yml b/.gitlab-ci-files/test.yml index d970f59..5f80b24 100644 --- a/.gitlab-ci-files/test.yml +++ b/.gitlab-ci-files/test.yml @@ -24,12 +24,6 @@ rocky9-test: - php artisan key:generate - vendor/bin/phpunit --log-junit $CI_PROJECT_DIR/flexiapi_phpunit.log -debian11-test: - extends: .debian-test - image: gitlab.linphone.org:4567/bc/public/docker/debian11-php:$DEBIAN_11_IMAGE_VERSION - needs: - - debian11-package - debian12-test: extends: .debian-test image: gitlab.linphone.org:4567/bc/public/docker/debian12-php:$DEBIAN_12_IMAGE_VERSION diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 08cc143..8640797 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,7 @@ variables: ROCKY_8_IMAGE_VERSION: 20241113_143521_update_php_82 ROCKY_9_IMAGE_VERSION: 20241114_161138_remove_redis - DEBIAN_11_IMAGE_VERSION: 20241112_113527_update_package_and_dependencies - DEBIAN_12_IMAGE_VERSION: 20241112_113948_update_package_and_dependencies + DEBIAN_12_IMAGE_VERSION: 20241112_113948_update_package_and_dependencies PHP_REDIS_REMI_VERSION: php-pecl-redis6-6.1.0-1 PHP_IGBINARY_REMI_VERSION: php-pecl-igbinary-3.2.16-2 PHP_MSGPACK_REMI_VERSION: php-pecl-msgpack-2.2.0-3 diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8348f..9f1b4e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ v1.7 ---- -- Fix FLEXIAPI-206 Upgrade to Laravel 10, PHP 8.1 minimum and bump all the related dependencies +- Fix FLEXIAPI-206 Upgrade to Laravel 10, PHP 8.1 minimum and bump all the related dependencies, drop Debian 11 Bullseye +- Fix FLEXIAPI-220 Migrate SIP Domains to Spaces v1.6 ---- @@ -29,7 +30,7 @@ v1.5 - Fix FLEXIAPI-185 Return null if the account dictionary is empty in the API - Fix FLEXIAPI-184 Append phone_change_code and email_change_code to the admin /accounts/ endpoint if they are available - Fix FLEXIAPI-183 Complete the account hooks on the dictionnary actions -- Fix FLEXIAPI-182 Replace APP_SUPER_ADMINS_SIP_DOMAINS with a proper sip_domains table, API endpoints, UI panels, console command, tests and documentation +- Fix FLEXIAPI-182 Replace APP_SUPER_ADMINS_SIP_DOMAINS with a proper spaces table, API endpoints, UI panels, console command, tests and documentation - Fix FLEXIAPI-181 Replace APP_ADMINS_MANAGE_MULTI_DOMAINS with APP_SUPER_ADMINS_SIP_DOMAINS - Fix FLEXIAPI-180 Fix the token and activation flow for the provisioning with token endpoint when the header is missing - Fix FLEXIAPI-179 Add Localization support as a Middleware that handles Accept-Language HTTP header diff --git a/Makefile b/Makefile index 9a0c120..6a03cd0 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ package-end-common: rm -rf $(OUTPUT_DIR)/rpmbuild/SPECS $(OUTPUT_DIR)/rpmbuild/SOURCES $(OUTPUT_DIR)/rpmbuild/SRPMS $(OUTPUT_DIR)/rpmbuild/BUILD $(OUTPUT_DIR)/rpmbuild/BUILDROOT rpm-el8-only: - sed -i 's/Requires:.*/Requires: php >= 8.0, php-gd, php-pdo, php-redis, php-mysqlnd, php-mbstring/g' $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec + sed -i 's/Requires:.*/Requires: php >= 8.1, php-gd, php-pdo, php-redis, php-mysqlnd, php-mbstring/g' $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec rpmbuild -v -bb --define 'dist .el8' --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec @echo "📦✅ RPM el8 Package Created" @@ -75,7 +75,7 @@ deb-only: fakeroot alien -g -k --scripts $(OUTPUT_DIR)/rpmbuild/tmp.rpm rm -r $(OUTPUT_DIR)/rpmbuild rm -rf $(OUTPUT_DIR)/*.orig - sed -i 's/Depends:.*/Depends: $${shlibs:Depends}, php (>= 8.0), php-xml, php-pdo, php-gd, php-redis, php-mysql, php-mbstring, php-sqlite3/g' $(OUTPUT_DIR)/bc-flexisip-account-manager*/debian/control + sed -i 's/Depends:.*/Depends: $${shlibs:Depends}, php (>= 8.1), php-xml, php-pdo, php-gd, php-redis, php-mysql, php-mbstring, php-sqlite3/g' $(OUTPUT_DIR)/bc-flexisip-account-manager*/debian/control cd `ls -rt $(OUTPUT_DIR) | tail -1` && dpkg-buildpackage --no-sign @echo "📦✅ DEB Package Created" diff --git a/README.md b/README.md index 5ef48e9..31b566d 100644 --- a/README.md +++ b/README.md @@ -148,11 +148,11 @@ FlexiAPI is also providing endpoints to provision Liblinphone powered devices. Y FlexiAPI is shipped with several console commands that you can launch using the `artisan` executable available at the root of this project. -### Create or update a SIP Domain +### Create or update a Space -Create or update a SIP Domain, required to then create accounts afterward. The `super` option enable/disable the domain as a super domain. +Create or update a Space, required to then create accounts afterward. The `super` option enable/disable the domain as a super domain. - php artisan sip_domains:create-update {domain} {--super} + php artisan spaces:create-update {domain} {--super} ### Create an admin account diff --git a/RELEASE.md b/RELEASE.md index e5632b5..248841d 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -17,7 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). - **New DotEnv variable:** `JWT_RSA_PUBLIC_KEY_PEM=` - **New DotEnv variable:** `JWT_SIP_IDENTIFIER=sip_identifier` - **Super-domains and super-admins support:** Introduce SIP domains management. The app accounts are now divided by their domains with their own respective administrators that can only see and manage their own domain accounts and settings. On top of that it is possible to configure a SIP domain as a "super-domain" and then allow its admins to become "super-admins". Those super-admins will then be able to manage all the accounts handled by the instance and create/edit/delete the other SIP domains. Add new endpoints and a new super-admin role in the API to manage the SIP domains. SIP domains can also be created and updated directly from the console using a new artisan script (documented in the README); - - **New Artisan script:** `php artisan sip_domains:create-update {domain} {--super}` + - **New Artisan script:** `php artisan spaces:create-update {domain} {--super}` - **Account Dictionary:** Each account can now handle a specific dictionary, configurable by the API or directly the web panel. This dictionary allows developers to store arbitrary `key -> value pairs` on each accounts. - **Vcard storage:** Attach custom vCards on a dedicated account using new endpoints in the API. The published vCard are validated before being stored. diff --git a/flexiapi/.env.example b/flexiapi/.env.example index da2e41d..126bb44 100644 --- a/flexiapi/.env.example +++ b/flexiapi/.env.example @@ -2,8 +2,7 @@ APP_NAME=FlexiAPI APP_ENV=local APP_KEY= APP_DEBUG=false -APP_URL=http://localhost -APP_SIP_DOMAIN=sip.example.com +APP_ROOT_DOMAIN= APP_LINPHONE_DAEMON_UNIX_PATH= APP_FLEXISIP_PUSHER_PATH= diff --git a/flexiapi/app/Account.php b/flexiapi/app/Account.php index bdba0ba..6e0e6ab 100644 --- a/flexiapi/app/Account.php +++ b/flexiapi/app/Account.php @@ -178,9 +178,9 @@ class Account extends Authenticatable return $this->belongsToMany(AccountType::class); } - public function sipDomain() + public function space() { - return $this->hasOne(SipDomain::class, 'domain', 'domain'); + return $this->hasOne(Space::class, 'domain', 'domain'); } public function statisticsFromCalls() @@ -336,7 +336,7 @@ class Account extends Authenticatable public function getSuperAdminAttribute(): bool { - return SipDomain::where('domain', $this->domain)->where('super', true)->exists() && $this->admin; + return Space::where('domain', $this->domain)->where('super', true)->exists() && $this->admin; } /** diff --git a/flexiapi/app/Console/Commands/Accounts/CreateAdminAccount.php b/flexiapi/app/Console/Commands/Accounts/CreateAdminAccount.php index 98335f2..9151e19 100644 --- a/flexiapi/app/Console/Commands/Accounts/CreateAdminAccount.php +++ b/flexiapi/app/Console/Commands/Accounts/CreateAdminAccount.php @@ -23,7 +23,7 @@ use Illuminate\Console\Command; use Carbon\Carbon; use App\Account; -use App\SipDomain; +use App\Space; class CreateAdminAccount extends Command { @@ -37,7 +37,7 @@ class CreateAdminAccount extends Command public function handle() { - $sipDomains = SipDomain::all('domain')->pluck('domain'); + $spaces = Space::all('domain')->pluck('domain'); $this->info('Your will create a new admin account in the database, existing accounts with the same credentials will be overwritten'); @@ -50,7 +50,7 @@ class CreateAdminAccount extends Command } if (!$this->option('domain')) { - $domain = $this->ask('What will be the admin domain? Default: ' . $sipDomains->first()); + $domain = $this->ask('What will be the admin domain? Default: ' . $spaces->first()); } if (!$this->option('password')) { @@ -58,11 +58,11 @@ class CreateAdminAccount extends Command } $username = $username ?? 'admin'; - $domain = $domain ?? $sipDomains->first(); + $domain = $domain ?? $spaces->first(); $password = $password ?? 'change_me'; - if (!$sipDomains->contains($domain)) { - $this->error('The domain must be one of the following ones: ' . $sipDomains->implode(', ')); + if (!$spaces->contains($domain)) { + $this->error('The domain must be one of the following ones: ' . $spaces->implode(', ')); $this->comment('You can create an extra domain using the dedicated console command'); return Command::FAILURE; } diff --git a/flexiapi/app/Console/Commands/Accounts/CreateAdminTest.php b/flexiapi/app/Console/Commands/Accounts/CreateAdminTest.php index cfd4921..27ea5af 100644 --- a/flexiapi/app/Console/Commands/Accounts/CreateAdminTest.php +++ b/flexiapi/app/Console/Commands/Accounts/CreateAdminTest.php @@ -40,7 +40,7 @@ class CreateAdminTest extends Command $username = 'admin_test'; $domain = 'sip.example.org'; - $this->call('sip_domains:create-update', [ + $this->call('spaces:create-update', [ 'domain' => $domain, '--super' => 'true' ]); diff --git a/flexiapi/app/Console/Commands/SipDomains/CreateUpdate.php b/flexiapi/app/Console/Commands/Spaces/CreateUpdate.php similarity index 68% rename from flexiapi/app/Console/Commands/SipDomains/CreateUpdate.php rename to flexiapi/app/Console/Commands/Spaces/CreateUpdate.php index 13e0883..564dc6c 100644 --- a/flexiapi/app/Console/Commands/SipDomains/CreateUpdate.php +++ b/flexiapi/app/Console/Commands/Spaces/CreateUpdate.php @@ -17,33 +17,34 @@ along with this program. If not, see . */ -namespace App\Console\Commands\SipDomains; +namespace App\Console\Commands\Spaces; -use App\SipDomain; +use App\Space; use Illuminate\Console\Command; class CreateUpdate extends Command { - protected $signature = 'sip_domains:create-update {domain} {--super}'; - protected $description = 'Create a SIP Domain'; + protected $signature = 'spaces:create-update {sip_domain} {host} {--super}'; + protected $description = 'Create a Space'; public function handle() { - $this->info('Your will create or update a SIP Domain in the database'); + $this->info('Your will create or update a Space in the database'); - $sipDomain = SipDomain::where('domain', $this->argument('domain'))->firstOrNew(); - $sipDomain->domain = $this->argument('domain'); + $space = Space::where('domain', $this->argument('sip_domain'))->firstOrNew(); + $space->host = $this->argument('host'); + $space->domain = $this->argument('sip_domain'); - $sipDomain->exists + $space->exists ? $this->info('The domain already exists, updating it') : $this->info('A new domain will be created'); - $sipDomain->super = (bool)$this->option('super'); - $sipDomain->super + $space->super = (bool)$this->option('super'); + $space->super ? $this->info('Set as a super domain') : $this->info('Set as a normal domain'); - $sipDomain->save(); + $space->save(); return Command::SUCCESS; } diff --git a/flexiapi/app/Helpers/Utils.php b/flexiapi/app/Helpers/Utils.php index 95962a8..784ca1b 100644 --- a/flexiapi/app/Helpers/Utils.php +++ b/flexiapi/app/Helpers/Utils.php @@ -42,7 +42,7 @@ function generateNonce(): string function getRequestBoolean(Request $request, string $key): bool { - return $request->has($key) ? $request->get($key) == "true" : false; + return $request->has($key) ? $request->get($key) == "on" : false; } function generateValidNonce(Account $account): string diff --git a/flexiapi/app/Http/Controllers/Account/AuthTokenController.php b/flexiapi/app/Http/Controllers/Account/AuthTokenController.php index e265ccd..bb4c1e0 100644 --- a/flexiapi/app/Http/Controllers/Account/AuthTokenController.php +++ b/flexiapi/app/Http/Controllers/Account/AuthTokenController.php @@ -72,7 +72,7 @@ class AuthTokenController extends Controller $authToken->delete(); - return redirect()->route('account.dashboard'); + return redirect()->route('account.home'); } /** @@ -87,6 +87,6 @@ class AuthTokenController extends Controller $authToken->save(); } - return redirect()->route('account.dashboard'); + return redirect()->route('account.home'); } } diff --git a/flexiapi/app/Http/Controllers/Account/AuthenticateController.php b/flexiapi/app/Http/Controllers/Account/AuthenticateController.php index 8f90ff9..fa92894 100644 --- a/flexiapi/app/Http/Controllers/Account/AuthenticateController.php +++ b/flexiapi/app/Http/Controllers/Account/AuthenticateController.php @@ -33,7 +33,13 @@ class AuthenticateController extends Controller public function login(Request $request) { - if (Auth::user()) { + if ($request->user()) { + if ($request->user()->superAdmin) { + return redirect()->route('admin.spaces.index'); + } elseif ($request->user()->admin) { + return redirect()->route('admin.spaces.me'); + } + return redirect()->route('account.dashboard'); } @@ -67,7 +73,7 @@ class AuthenticateController extends Controller bchash($account->username, $account->resolvedRealm, $request->get('password'), $password->algorithm) )) { Auth::login($account); - return redirect()->route('account.dashboard'); + return redirect()->route('account.home'); } } @@ -94,7 +100,7 @@ class AuthenticateController extends Controller Auth::login($account); - return redirect()->route('account.dashboard'); + return redirect()->route('account.home'); } public function loginAuthToken(Request $request, ?string $token = null) @@ -120,7 +126,7 @@ class AuthenticateController extends Controller $authToken->delete(); - return redirect()->route('account.dashboard'); + return redirect()->route('account.home'); } return view('account.authenticate.auth_token', [ diff --git a/flexiapi/app/Http/Controllers/Account/ProvisioningController.php b/flexiapi/app/Http/Controllers/Account/ProvisioningController.php index bcd59df..ed122e5 100644 --- a/flexiapi/app/Http/Controllers/Account/ProvisioningController.php +++ b/flexiapi/app/Http/Controllers/Account/ProvisioningController.php @@ -206,7 +206,7 @@ class ProvisioningController extends Controller if ($account) { $ui = $xpath->query("//section[@name='ui']")->item(0); - if ($ui == null && $account->sipDomain) { + if ($ui == null && $account->space) { $section = $dom->createElement('section'); $section->setAttribute('name', 'ui'); @@ -225,7 +225,7 @@ class ProvisioningController extends Controller 'max_account', ] as $key) { // Cast the booleans into integers - $entry = $dom->createElement('entry', (int)$account->sipDomain->$key); + $entry = $dom->createElement('entry', (int)$account->space->$key); $entry->setAttribute('name', $key); $section->appendChild($entry); } diff --git a/flexiapi/app/Http/Controllers/Admin/AccountController.php b/flexiapi/app/Http/Controllers/Admin/AccountController.php index 7d1aea5..01c19f0 100644 --- a/flexiapi/app/Http/Controllers/Admin/AccountController.php +++ b/flexiapi/app/Http/Controllers/Admin/AccountController.php @@ -28,7 +28,7 @@ use App\ContactsList; use App\Http\Requests\Account\Create\Web\AsAdminRequest; use App\Http\Requests\Account\Update\Web\AsAdminRequest as WebAsAdminRequest; use App\Services\AccountService; -use App\SipDomain; +use App\Space; class AccountController extends Controller { @@ -61,6 +61,9 @@ class AccountController extends Controller } return view('admin.account.index', [ + 'space' => (!$request->user()->superAdmin) + ? $request->user()->space + : null, 'domains' => Account::groupBy('domain')->pluck('domain'), 'contacts_lists' => ContactsList::all()->pluck('title', 'id'), 'accounts' => $accounts->paginate(20)->appends($request->query()), @@ -74,11 +77,21 @@ class AccountController extends Controller public function create(Request $request) { + $account = new Account; + + if ($request->has('admin')) { + $account->admin = true; + } + + if ($request->has('domain')) { + $account->domain = $request->get('domain'); + } + return view('admin.account.create_edit', [ - 'account' => new Account, + 'account' => $account, 'domains' => $request->user()?->superAdmin - ? SipDomain::all() - : SipDomain::where('domain', $request->user()->domain)->get(), + ? Space::notFull()->get() + : Space::where('domain', $request->user()->domain)->get(), 'protocols' => [null => 'None'] + Account::$dtmfProtocols ]); } @@ -100,8 +113,8 @@ class AccountController extends Controller 'account' => $account, 'protocols' => [null => 'None'] + Account::$dtmfProtocols, 'domains' => $request->user()?->superAdmin - ? SipDomain::all() - : SipDomain::where('domain', $account->domain)->get(), + ? Space::all() + : Space::where('domain', $account->domain)->get(), 'contacts_lists' => ContactsList::whereNotIn('id', function ($query) use ($accountId) { $query->select('contacts_list_id') ->from('account_contacts_list') diff --git a/flexiapi/app/Http/Controllers/Admin/SipDomainController.php b/flexiapi/app/Http/Controllers/Admin/SipDomainController.php deleted file mode 100644 index 9638ff8..0000000 --- a/flexiapi/app/Http/Controllers/Admin/SipDomainController.php +++ /dev/null @@ -1,120 +0,0 @@ -. -*/ - -namespace App\Http\Controllers\Admin; - -use Illuminate\Http\Request; - -use App\Http\Controllers\Controller; -use App\SipDomain; -use Illuminate\Validation\Rule; - -class SipDomainController extends Controller -{ - public function index() - { - return view('admin.sip_domain.index', ['sip_domains' => SipDomain::withCount('accounts')->get()]); - } - - public function create() - { - return view('admin.sip_domain.create_edit', [ - 'sip_domain' => new SipDomain - ]); - } - - public function store(Request $request) - { - $request->validate([ - 'domain' => 'required|unique:sip_domains', - ]); - - $sipDomain = new SipDomain; - $sipDomain->domain = $request->get('domain'); - $sipDomain = $this->setConfig($request, $sipDomain); - $sipDomain->save(); - - return redirect()->route('admin.sip_domains.index'); - } - - public function edit(int $id) - { - return view('admin.sip_domain.create_edit', [ - 'sip_domain' => SipDomain::findOrFail($id) - ]); - } - - public function update(Request $request, int $id) - { - $request->validate([ - 'max_account' => 'required|integer', - ]); - - $sipDomain = SipDomain::findOrFail($id); - $sipDomain = $this->setConfig($request, $sipDomain); - $sipDomain->save(); - - return redirect()->back(); - } - - private function setConfig(Request $request, SipDomain $sipDomain) - { - $request->validate([ - 'max_account' => 'required|integer', - ]); - - $sipDomain->super = getRequestBoolean($request, 'super'); - $sipDomain->disable_chat_feature = getRequestBoolean($request, 'disable_chat_feature'); - $sipDomain->disable_meetings_feature = getRequestBoolean($request, 'disable_meetings_feature'); - $sipDomain->disable_broadcast_feature = getRequestBoolean($request, 'disable_broadcast_feature'); - $sipDomain->hide_settings = getRequestBoolean($request, 'hide_settings'); - $sipDomain->max_account = $request->get('max_account', 0); - $sipDomain->hide_account_settings = getRequestBoolean($request, 'hide_account_settings'); - $sipDomain->disable_call_recordings_feature = getRequestBoolean($request, 'disable_call_recordings_feature'); - $sipDomain->only_display_sip_uri_username = getRequestBoolean($request, 'only_display_sip_uri_username'); - $sipDomain->assistant_hide_create_account = getRequestBoolean($request, 'assistant_hide_create_account'); - $sipDomain->assistant_disable_qr_code = getRequestBoolean($request, 'assistant_disable_qr_code'); - $sipDomain->assistant_hide_third_party_account = getRequestBoolean($request, 'assistant_hide_third_party_account'); - - return $sipDomain; - } - - public function delete(int $id) - { - return view('admin.sip_domain.delete', [ - 'sip_domain' => SipDomain::findOrFail($id) - ]); - } - - public function destroy(Request $request, int $id) - { - $sipDomain = SipDomain::findOrFail($id); - - $request->validate([ - 'domain' => [ - 'required', - Rule::in(['first-zone', $sipDomain->domain]), - ] - ]); - - $sipDomain->delete(); - - return redirect()->route('admin.sip_domains.index'); - } -} diff --git a/flexiapi/app/Http/Controllers/Admin/SpaceController.php b/flexiapi/app/Http/Controllers/Admin/SpaceController.php new file mode 100644 index 0000000..9f0346b --- /dev/null +++ b/flexiapi/app/Http/Controllers/Admin/SpaceController.php @@ -0,0 +1,164 @@ +. +*/ + +namespace App\Http\Controllers\Admin; + +use Illuminate\Http\Request; + +use App\Http\Controllers\Controller; +use App\Space; +use Illuminate\Validation\Rule; + +class SpaceController extends Controller +{ + public function index() + { + return view('admin.space.index', ['spaces' => Space::withCount('accounts')->get()]); + } + + public function me(Request $request) + { + return view('admin.space.show', [ + 'space' => $request->user()->space + ]); + } + + public function show(Space $space) + { + return view('admin.space.show', [ + 'space' => $space + ]); + } + + public function create() + { + return view('admin.space.create', [ + 'space' => new Space() + ]); + } + + public function store(Request $request) + { + $request->merge(['full_host' => $request->get('host') . '.' . config('app.root_domain')]); + $request->validate([ + 'domain' => 'required|unique:spaces|regex:/'. Space::DOMAIN_REGEX . '/', + 'host' => 'required|regex:/'. Space::HOST_REGEX . '/', + 'full_host' => 'required|unique:spaces,host', + ]); + + $space = new Space(); + $space->domain = $request->get('domain'); + $space->host = $request->get('full_host'); + $space->save(); + + return redirect()->route('admin.spaces.index'); + } + + public function edit(Space $space) + { + return view('admin.space.edit', [ + 'space' => $space + ]); + } + + public function update(Request $request, Space $space) + { + $request->validate([ + 'max_account' => 'required|integer', + ]); + + $space = $this->setConfig($request, $space); + $space->save(); + + return redirect()->back(); + } + + public function parameters(Space $space) + { + return view('admin.space.parameters', [ + 'space' => $space + ]); + } + + public function parametersUpdate(Request $request, Space $space) + { + $request->validate([ + 'max_accounts' => 'required|integer|min:0', + 'expire_at' => 'nullable|date|after_or_equal:today' + ]); + + if ($request->get('max_accounts') > 0) { + $request->validate([ + 'max_accounts' => 'integer|min:' . $space->accounts()->count() + ]); + } + + $space->super = getRequestBoolean($request, 'super'); + $space->max_accounts = $request->get('max_accounts'); + $space->expire_at = $request->get('expire_at'); + $space->save(); + + return redirect()->route('admin.spaces.show', $space); + } + + private function setConfig(Request $request, Space $space) + { + $request->validate([ + 'max_account' => 'required|integer', + ]); + + $space->disable_chat_feature = getRequestBoolean($request, 'disable_chat_feature'); + $space->disable_meetings_feature = getRequestBoolean($request, 'disable_meetings_feature'); + $space->disable_broadcast_feature = getRequestBoolean($request, 'disable_broadcast_feature'); + $space->hide_settings = getRequestBoolean($request, 'hide_settings'); + $space->max_account = $request->get('max_account', 0); + $space->hide_account_settings = getRequestBoolean($request, 'hide_account_settings'); + $space->disable_call_recordings_feature = getRequestBoolean($request, 'disable_call_recordings_feature'); + $space->only_display_sip_uri_username = getRequestBoolean($request, 'only_display_sip_uri_username'); + $space->assistant_hide_create_account = getRequestBoolean($request, 'assistant_hide_create_account'); + $space->assistant_disable_qr_code = getRequestBoolean($request, 'assistant_disable_qr_code'); + $space->assistant_hide_third_party_account = getRequestBoolean($request, 'assistant_hide_third_party_account'); + + return $space; + } + + public function delete(Request $request, int $id) + { + $space = Space::findOrFail($id); + return view('admin.space.delete', [ + 'space' => $space + ]); + } + + public function destroy(Request $request, int $id) + { + $space = Space::findOrFail($id); + + $request->validate([ + 'domain' => [ + 'required', + Rule::in(['first-zone', $space->domain]), + ] + ]); + + $space->delete(); + + return redirect()->route('admin.spaces.index'); + } +} diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php b/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php index 7455cb1..abefc63 100644 --- a/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php +++ b/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php @@ -30,7 +30,7 @@ use App\ContactsList; use App\Http\Requests\Account\Create\Api\AsAdminRequest; use App\Http\Requests\Account\Update\Api\AsAdminRequest as ApiAsAdminRequest; use App\Services\AccountService; -use App\SipDomain; +use App\Space; class AccountController extends Controller { @@ -139,14 +139,15 @@ class AccountController extends Controller public function store(AsAdminRequest $request) { - // Create the missing SipDomain - if ($request->user()->superAdmin + // Create the missing Space + /*if ($request->user()->superAdmin && $request->has('domain') - && !SipDomain::pluck('domain')->contains($request->get('domain'))) { - $sipDomain = new SipDomain(); - $sipDomain->domain = $request->get('domain'); - $sipDomain->save(); - } + && !Space::pluck('domain')->contains($request->get('domain'))) { + $space = new Space(); + $space->domain = $request->get('domain'); + $space->host = $request->get('host'); + $space->save(); + }*/ return (new AccountService())->store($request)->makeVisible(['confirmation_key', 'provisioning_token']); } diff --git a/flexiapi/app/Http/Controllers/Api/Admin/SipDomainController.php b/flexiapi/app/Http/Controllers/Api/Admin/SipDomainController.php deleted file mode 100644 index 38291e7..0000000 --- a/flexiapi/app/Http/Controllers/Api/Admin/SipDomainController.php +++ /dev/null @@ -1,111 +0,0 @@ -. -*/ - -namespace App\Http\Controllers\Api\Admin; - -use Illuminate\Http\Request; - -use App\Http\Controllers\Controller; -use App\SipDomain; - -class SipDomainController extends Controller -{ - public function index() - { - return SipDomain::all(); - } - - public function store(Request $request) - { - $request->validate([ - 'domain' => 'required|unique:sip_domains', - ]); - - $sipDomain = new SipDomain; - $sipDomain->domain = $request->get('domain'); - $this->setRequestBoolean($request, $sipDomain, 'super'); - $this->setRequestBoolean($request, $sipDomain, 'disable_chat_feature'); - $this->setRequestBoolean($request, $sipDomain, 'disable_meetings_feature'); - $this->setRequestBoolean($request, $sipDomain, 'disable_broadcast_feature'); - $this->setRequestBoolean($request, $sipDomain, 'hide_settings'); - $this->setRequestBoolean($request, $sipDomain, 'hide_account_settings'); - $this->setRequestBoolean($request, $sipDomain, 'disable_call_recordings_feature'); - $this->setRequestBoolean($request, $sipDomain, 'only_display_sip_uri_username'); - $this->setRequestBoolean($request, $sipDomain, 'assistant_hide_create_account'); - $this->setRequestBoolean($request, $sipDomain, 'assistant_disable_qr_code'); - $this->setRequestBoolean($request, $sipDomain, 'assistant_hide_third_party_account'); - $sipDomain->max_account = $request->get('max_account', 0); - - $sipDomain->save(); - - return $sipDomain->refresh(); - } - - public function show(string $domain) - { - return SipDomain::where('domain', $domain)->firstOrFail(); - } - - public function update(Request $request, string $domain) - { - $request->validate([ - 'super' => 'required|boolean', - 'disable_chat_feature' => 'required|boolean', - 'disable_meetings_feature' => 'required|boolean', - 'disable_broadcast_feature' => 'required|boolean', - 'hide_settings' => 'required|boolean', - 'hide_account_settings' => 'required|boolean', - 'disable_call_recordings_feature' => 'required|boolean', - 'only_display_sip_uri_username' => 'required|boolean', - 'assistant_hide_create_account' => 'required|boolean', - 'assistant_disable_qr_code' => 'required|boolean', - 'assistant_hide_third_party_account' => 'required|boolean', - 'max_account' => 'required|integer', - ]); - - $sipDomain = SipDomain::where('domain', $domain)->firstOrFail(); - $sipDomain->super = $request->get('super'); - $sipDomain->disable_chat_feature = $request->get('disable_chat_feature'); - $sipDomain->disable_meetings_feature = $request->get('disable_meetings_feature'); - $sipDomain->disable_broadcast_feature = $request->get('disable_broadcast_feature'); - $sipDomain->hide_settings = $request->get('hide_settings'); - $sipDomain->hide_account_settings = $request->get('hide_account_settings'); - $sipDomain->disable_call_recordings_feature = $request->get('disable_call_recordings_feature'); - $sipDomain->only_display_sip_uri_username = $request->get('only_display_sip_uri_username'); - $sipDomain->assistant_hide_create_account = $request->get('assistant_hide_create_account'); - $sipDomain->assistant_disable_qr_code = $request->get('assistant_disable_qr_code'); - $sipDomain->assistant_hide_third_party_account = $request->get('assistant_hide_third_party_account'); - $sipDomain->max_account = $request->get('max_account', 0); - $sipDomain->save(); - - return $sipDomain; - } - - private function setRequestBoolean(Request $request, SipDomain $sipDomain, string $key) - { - if ($request->has($key)) { - $sipDomain->$key = (bool)$request->get($key); - } - } - - public function destroy(string $domain) - { - return SipDomain::where('domain', $domain)->delete(); - } -} diff --git a/flexiapi/app/Http/Controllers/Api/Admin/SpaceController.php b/flexiapi/app/Http/Controllers/Api/Admin/SpaceController.php new file mode 100644 index 0000000..c5c0b5a --- /dev/null +++ b/flexiapi/app/Http/Controllers/Api/Admin/SpaceController.php @@ -0,0 +1,134 @@ +. +*/ + +namespace App\Http\Controllers\Api\Admin; + +use App\Space; +use App\Http\Controllers\Controller; + +use Illuminate\Http\Request; +use Illuminate\Validation\Rule; + +class SpaceController extends Controller +{ + public function index() + { + return Space::all(); + } + + public function store(Request $request) + { + $request->validate([ + 'domain' => 'required|unique:spaces', + 'host' => 'required|unique:spaces', + 'max_accounts' => 'nullable|integer', + 'expire_at' => 'nullable|date|after_or_equal:today' + ]); + + $space = new Space; + $space->domain = $request->get('domain'); + $space->host = $request->get('host'); + $this->setRequestBoolean($request, $space, 'super'); + $this->setRequestBoolean($request, $space, 'disable_chat_feature'); + $this->setRequestBoolean($request, $space, 'disable_meetings_feature'); + $this->setRequestBoolean($request, $space, 'disable_broadcast_feature'); + $this->setRequestBoolean($request, $space, 'hide_settings'); + $this->setRequestBoolean($request, $space, 'hide_account_settings'); + $this->setRequestBoolean($request, $space, 'disable_call_recordings_feature'); + $this->setRequestBoolean($request, $space, 'only_display_sip_uri_username'); + $this->setRequestBoolean($request, $space, 'assistant_hide_create_account'); + $this->setRequestBoolean($request, $space, 'assistant_disable_qr_code'); + $this->setRequestBoolean($request, $space, 'assistant_hide_third_party_account'); + $space->max_account = $request->get('max_account', 0); + $space->max_accounts = $request->get('max_accounts', 0); + $space->expire_at = $request->get('expire_at'); + + $space->save(); + + return $space->refresh(); + } + + public function show(string $domain) + { + return Space::where('domain', $domain)->firstOrFail(); + } + + public function update(Request $request, string $domain) + { + $request->validate([ + 'super' => 'required|boolean', + 'disable_chat_feature' => 'required|boolean', + 'disable_meetings_feature' => 'required|boolean', + 'disable_broadcast_feature' => 'required|boolean', + 'hide_settings' => 'required|boolean', + 'hide_account_settings' => 'required|boolean', + 'disable_call_recordings_feature' => 'required|boolean', + 'only_display_sip_uri_username' => 'required|boolean', + 'assistant_hide_create_account' => 'required|boolean', + 'assistant_disable_qr_code' => 'required|boolean', + 'assistant_hide_third_party_account' => 'required|boolean', + 'max_account' => 'required|integer', + 'max_accounts' => 'required|integer', + 'expire_at' => 'nullable|date|after_or_equal:today', + ]); + + $space = Space::where('domain', $domain)->firstOrFail(); + + if ($request->get('max_accounts') > 0) { + $request->validate([ + 'max_accounts' => 'integer|min:' . $space->accounts()->count() + ]); + } + + $request->validate([ + 'host' => ['required', Rule::unique('spaces')->ignore($space->id)] + ]); + + $space->host = $request->get('host'); + $space->super = $request->get('super'); + $space->disable_chat_feature = $request->get('disable_chat_feature'); + $space->disable_meetings_feature = $request->get('disable_meetings_feature'); + $space->disable_broadcast_feature = $request->get('disable_broadcast_feature'); + $space->hide_settings = $request->get('hide_settings'); + $space->hide_account_settings = $request->get('hide_account_settings'); + $space->disable_call_recordings_feature = $request->get('disable_call_recordings_feature'); + $space->only_display_sip_uri_username = $request->get('only_display_sip_uri_username'); + $space->assistant_hide_create_account = $request->get('assistant_hide_create_account'); + $space->assistant_disable_qr_code = $request->get('assistant_disable_qr_code'); + $space->assistant_hide_third_party_account = $request->get('assistant_hide_third_party_account'); + $space->max_account = $request->get('max_account', 0); + $space->max_accounts = $request->get('max_accounts', 0); + $space->expire_at = $request->get('expire_at'); + $space->save(); + + return $space; + } + + private function setRequestBoolean(Request $request, Space $space, string $key) + { + if ($request->has($key)) { + $space->$key = (bool)$request->get($key); + } + } + + public function destroy(string $domain) + { + return Space::where('domain', $domain)->delete(); + } +} diff --git a/flexiapi/app/Http/Kernel.php b/flexiapi/app/Http/Kernel.php index bc3c5bd..87075b4 100644 --- a/flexiapi/app/Http/Kernel.php +++ b/flexiapi/app/Http/Kernel.php @@ -36,6 +36,7 @@ class Kernel extends HttpKernel \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + \App\Http\Middleware\Space::class ]; /** @@ -59,6 +60,7 @@ class Kernel extends HttpKernel 'bindings', 'validate_json', 'localization', + 'space' ], ]; @@ -87,6 +89,8 @@ class Kernel extends HttpKernel 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, + 'space' => \App\Http\Middleware\Space::class, + 'space.expired' => \App\Http\Middleware\IsSpaceExpired::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'localization' => \App\Http\Middleware\Localization::class, diff --git a/flexiapi/app/Http/Middleware/IsSpaceExpired.php b/flexiapi/app/Http/Middleware/IsSpaceExpired.php new file mode 100644 index 0000000..446db97 --- /dev/null +++ b/flexiapi/app/Http/Middleware/IsSpaceExpired.php @@ -0,0 +1,19 @@ +user() && !$request->user()->superAdmin && $request->get('resolvedSpace')?->isExpired()) { + abort(403, 'The related Space has expired'); + } + + return $next($request); + } +} diff --git a/flexiapi/app/Http/Middleware/Space.php b/flexiapi/app/Http/Middleware/Space.php new file mode 100644 index 0000000..1c77c7c --- /dev/null +++ b/flexiapi/app/Http/Middleware/Space.php @@ -0,0 +1,35 @@ +header('host'))->first(); + + if ($space) { + if (!str_ends_with($space->host, config('app.root_domain'))) { + return abort(503, 'The APP_ROOT_DOMAIN configured does not match with the current root domain'); + } + + Config::set('app.url', '://' . $space->host); + Config::set('app.sip_domain', $space->domain); + + $request->request->set('resolvedSpace', $space); + + return $next($request); + } + + return abort(404, 'Host not configured'); + } +} diff --git a/flexiapi/app/Http/Requests/Account/Create/Api/AsAdminRequest.php b/flexiapi/app/Http/Requests/Account/Create/Api/AsAdminRequest.php index ac809b1..333ecb0 100644 --- a/flexiapi/app/Http/Requests/Account/Create/Api/AsAdminRequest.php +++ b/flexiapi/app/Http/Requests/Account/Create/Api/AsAdminRequest.php @@ -29,11 +29,6 @@ class AsAdminRequest extends Request { use RequestsApi, AsAdmin; - public function authorize() - { - return true; - } - public function rules() { $rules = parent::rules(); @@ -46,10 +41,6 @@ class AsAdminRequest extends Request 'nullable', ]; - if ($this->user()->superAdmin) { - $rules['domain'] = ''; - } - if (config('app.allow_phone_number_username_admin_api') == true) { array_splice( $rules['username'], diff --git a/flexiapi/app/Http/Requests/Account/Create/Api/Request.php b/flexiapi/app/Http/Requests/Account/Create/Api/Request.php index f948298..82dd4b9 100644 --- a/flexiapi/app/Http/Requests/Account/Create/Api/Request.php +++ b/flexiapi/app/Http/Requests/Account/Create/Api/Request.php @@ -28,11 +28,6 @@ class Request extends CreateRequest { use RequestsApi; - public function authorize() - { - return true; - } - public function rules() { $rules = parent::rules(); diff --git a/flexiapi/app/Http/Requests/Account/Create/Request.php b/flexiapi/app/Http/Requests/Account/Create/Request.php index 18cc7a2..b0300a7 100644 --- a/flexiapi/app/Http/Requests/Account/Create/Request.php +++ b/flexiapi/app/Http/Requests/Account/Create/Request.php @@ -23,6 +23,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; use App\Account; +use App\Space; use App\Rules\BlacklistedUsername; use App\Rules\Dictionary; use App\Rules\FilteredPhone; @@ -34,7 +35,8 @@ class Request extends FormRequest { public function authorize() { - return true; + $space = Space::where('domain', resolveDomain($this))->first(); + return ($space && !$space->isFull()); } public function rules() @@ -54,7 +56,7 @@ class Request extends FormRequest }), 'filled', ], - 'domain' => 'exists:sip_domains,domain', + 'domain' => 'exists:spaces,domain', 'dictionary' => [new Dictionary()], 'password' => 'required|min:3', 'email' => config('app.account_email_unique') diff --git a/flexiapi/app/Http/Requests/Account/Create/Web/AsAdminRequest.php b/flexiapi/app/Http/Requests/Account/Create/Web/AsAdminRequest.php index b03a3b3..53e76e3 100644 --- a/flexiapi/app/Http/Requests/Account/Create/Web/AsAdminRequest.php +++ b/flexiapi/app/Http/Requests/Account/Create/Web/AsAdminRequest.php @@ -27,11 +27,6 @@ class AsAdminRequest extends CreateRequest { use AsAdmin; - public function authorize() - { - return true; - } - public function rules() { $rules = parent::rules(); diff --git a/flexiapi/app/Http/Requests/Account/Create/Web/Request.php b/flexiapi/app/Http/Requests/Account/Create/Web/Request.php index 3572eee..d3a20fe 100644 --- a/flexiapi/app/Http/Requests/Account/Create/Web/Request.php +++ b/flexiapi/app/Http/Requests/Account/Create/Web/Request.php @@ -23,11 +23,6 @@ use App\Http\Requests\Account\Create\Request as CreateRequest; class Request extends CreateRequest { - public function authorize() - { - return true; - } - public function rules() { $rules = parent::rules(); diff --git a/flexiapi/app/Http/Requests/Account/Update/Api/AsAdminRequest.php b/flexiapi/app/Http/Requests/Account/Update/Api/AsAdminRequest.php index 2345dc9..6209c0d 100644 --- a/flexiapi/app/Http/Requests/Account/Update/Api/AsAdminRequest.php +++ b/flexiapi/app/Http/Requests/Account/Update/Api/AsAdminRequest.php @@ -29,11 +29,6 @@ class AsAdminRequest extends UpdateRequest { use RequestsApi, AsAdmin; - public function authorize() - { - return true; - } - public function rules() { $rules = parent::rules(); diff --git a/flexiapi/app/Http/Requests/Account/Update/Request.php b/flexiapi/app/Http/Requests/Account/Update/Request.php index eeb5199..e865463 100644 --- a/flexiapi/app/Http/Requests/Account/Update/Request.php +++ b/flexiapi/app/Http/Requests/Account/Update/Request.php @@ -50,7 +50,7 @@ class Request extends FormRequest })->ignore($this->route('account_id'), 'id'), 'filled', ], - 'domain' => 'exists:sip_domains,domain', + 'domain' => 'exists:spaces,domain', 'email' => [ 'nullable', 'email', diff --git a/flexiapi/app/Http/Requests/Account/Update/Web/AsAdminRequest.php b/flexiapi/app/Http/Requests/Account/Update/Web/AsAdminRequest.php index 644848f..b2b3700 100644 --- a/flexiapi/app/Http/Requests/Account/Update/Web/AsAdminRequest.php +++ b/flexiapi/app/Http/Requests/Account/Update/Web/AsAdminRequest.php @@ -27,11 +27,6 @@ class AsAdminRequest extends UpdateRequest { use AsAdmin; - public function authorize() - { - return true; - } - public function rules() { $rules = parent::rules(); diff --git a/flexiapi/app/Providers/AppServiceProvider.php b/flexiapi/app/Providers/AppServiceProvider.php index 91bb3f2..a0ef0d4 100644 --- a/flexiapi/app/Providers/AppServiceProvider.php +++ b/flexiapi/app/Providers/AppServiceProvider.php @@ -15,11 +15,5 @@ class AppServiceProvider extends ServiceProvider public function boot() { Validator::extend('iso_date', 'validateIsoDate'); - - if (!empty(config('app.url'))) { - // Add following lines to force laravel to use APP_URL as root url for the app. - $strBaseURL = $this->app['url']; - $strBaseURL->forceRootUrl(config('app.url')); - } } } diff --git a/flexiapi/app/Providers/RouteServiceProvider.php b/flexiapi/app/Providers/RouteServiceProvider.php index 527eee3..e348f1d 100644 --- a/flexiapi/app/Providers/RouteServiceProvider.php +++ b/flexiapi/app/Providers/RouteServiceProvider.php @@ -30,8 +30,6 @@ class RouteServiceProvider extends ServiceProvider */ public function boot() { - // - parent::boot(); } @@ -43,10 +41,7 @@ class RouteServiceProvider extends ServiceProvider public function map() { $this->mapApiRoutes(); - $this->mapWebRoutes(); - - // } /** diff --git a/flexiapi/app/SipDomain.php b/flexiapi/app/SipDomain.php deleted file mode 100644 index dc566a5..0000000 --- a/flexiapi/app/SipDomain.php +++ /dev/null @@ -1,31 +0,0 @@ - 'boolean', - 'disable_chat_feature' => 'boolean', - 'disable_meetings_feature' => 'boolean', - 'disable_broadcast_feature' => 'boolean', - 'hide_settings' => 'boolean', - 'hide_account_settings' => 'boolean', - 'disable_call_recordings_feature' => 'boolean', - 'only_display_sip_uri_username' => 'boolean', - 'assistant_hide_create_account' => 'boolean', - 'assistant_disable_qr_code' => 'boolean', - 'assistant_hide_third_party_account' => 'boolean', - ]; - - public function accounts() - { - return $this->hasMany(Account::class, 'domain', 'domain'); - } -} diff --git a/flexiapi/app/Space.php b/flexiapi/app/Space.php new file mode 100644 index 0000000..5837177 --- /dev/null +++ b/flexiapi/app/Space.php @@ -0,0 +1,89 @@ + 'boolean', + 'disable_chat_feature' => 'boolean', + 'disable_meetings_feature' => 'boolean', + 'disable_broadcast_feature' => 'boolean', + 'hide_settings' => 'boolean', + 'hide_account_settings' => 'boolean', + 'disable_call_recordings_feature' => 'boolean', + 'only_display_sip_uri_username' => 'boolean', + 'assistant_hide_create_account' => 'boolean', + 'assistant_disable_qr_code' => 'boolean', + 'assistant_hide_third_party_account' => 'boolean', + 'expire_at' => 'date', + ]; + + public const HOST_REGEX = '[\w\-]+'; + public const DOMAIN_REGEX = '[\w\-\.]+'; + + public function accounts() + { + return $this->hasMany(Account::class, 'domain', 'domain'); + } + + public function admins() + { + return $this->accounts()->where('admin', true); + } + + public function scopeNotFull(Builder $query) + { + return $query->where('max_accounts', 0) + ->orWhereRaw('max_accounts > (select count(*) from accounts where domain = spaces.domain)'); + } + + public function getAccountsPercentageAttribute(): int + { + if ($this->max_accounts != null) { + return (int)($this->accounts()->count() / $this->max_accounts * 100); + } + + return 0; + } + + public function isFull(): bool + { + return $this->max_accounts > 0 && ($this->accounts()->count() >= $this->max_accounts); + } + + public function isExpired(): bool + { + return $this->expire_at && $this->expire_at->isPast(); + } + + public function getAccountsPercentageClassAttribute(): string + { + if ($this->getAccountsPercentageAttribute() >= 80) { + return 'orange'; + } + + if ($this->getAccountsPercentageAttribute() >= 60) { + return 'yellow'; + } + + return 'green'; + } + + public function getDaysLeftAttribute(): ?int + { + if ($this->expire_at != null) { + return (int)$this->expire_at->diffInDays(Carbon::now()); + } + + return null; + } +} diff --git a/flexiapi/composer.lock b/flexiapi/composer.lock index a2f0621..a2f95b1 100644 --- a/flexiapi/composer.lock +++ b/flexiapi/composer.lock @@ -1191,16 +1191,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.24.0", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "a136842a532bac9ecd8a1c723852b09915d7db50" + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/a136842a532bac9ecd8a1c723852b09915d7db50", - "reference": "a136842a532bac9ecd8a1c723852b09915d7db50", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { @@ -1248,9 +1248,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "time": "2024-11-07T15:11:20+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { "name": "fruitcake/php-cors", @@ -1325,16 +1325,16 @@ }, { "name": "giggsey/libphonenumber-for-php-lite", - "version": "8.13.49", + "version": "8.13.50", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", - "reference": "f2bc102ccd614fde128b463273ce9843e14bf430" + "reference": "57bb2bfd8d4a9896ed961c584141247f2a35bc04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/f2bc102ccd614fde128b463273ce9843e14bf430", - "reference": "f2bc102ccd614fde128b463273ce9843e14bf430", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/57bb2bfd8d4a9896ed961c584141247f2a35bc04", + "reference": "57bb2bfd8d4a9896ed961c584141247f2a35bc04", "shasum": "" }, "require": { @@ -1404,7 +1404,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", "source": "https://github.com/giggsey/libphonenumber-for-php-lite" }, - "time": "2024-11-04T10:42:41+00:00" + "time": "2024-11-18T09:58:30+00:00" }, { "name": "graham-campbell/result-type", @@ -1881,16 +1881,16 @@ }, { "name": "laravel/framework", - "version": "v10.48.23", + "version": "v10.48.25", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "625269ca4881d2b50eded2045cb930960a181d98" + "reference": "f132b23b13909cc22c615c01b0c5640541c3da0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/625269ca4881d2b50eded2045cb930960a181d98", - "reference": "625269ca4881d2b50eded2045cb930960a181d98", + "url": "https://api.github.com/repos/laravel/framework/zipball/f132b23b13909cc22c615c01b0c5640541c3da0c", + "reference": "f132b23b13909cc22c615c01b0c5640541c3da0c", "shasum": "" }, "require": { @@ -1997,7 +1997,7 @@ "nyholm/psr7": "^1.2", "orchestra/testbench-core": "^8.23.4", "pda/pheanstalk": "^4.0", - "phpstan/phpstan": "^1.4.7", + "phpstan/phpstan": "~1.11.11", "phpunit/phpunit": "^10.0.7", "predis/predis": "^2.0.2", "symfony/cache": "^6.2", @@ -2084,7 +2084,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-11-12T15:39:10+00:00" + "time": "2024-11-26T15:32:57+00:00" }, { "name": "laravel/prompts", @@ -2146,16 +2146,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v1.3.6", + "version": "v1.3.7", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f865a58ea3a0107c336b7045104c75243fa59d96" + "reference": "4f48ade902b94323ca3be7646db16209ec76be3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f865a58ea3a0107c336b7045104c75243fa59d96", - "reference": "f865a58ea3a0107c336b7045104c75243fa59d96", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/4f48ade902b94323ca3be7646db16209ec76be3d", + "reference": "4f48ade902b94323ca3be7646db16209ec76be3d", "shasum": "" }, "require": { @@ -2203,7 +2203,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-11-11T17:06:04+00:00" + "time": "2024-11-14T18:34:49+00:00" }, { "name": "laravel/tinker", @@ -3394,32 +3394,32 @@ }, { "name": "nunomaduro/termwind", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "dcf1ec3dfa36137b7ce41d43866644a7ab8fc257" + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dcf1ec3dfa36137b7ce41d43866644a7ab8fc257", - "reference": "dcf1ec3dfa36137b7ce41d43866644a7ab8fc257", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.1", - "symfony/console": "^6.4.12" + "symfony/console": "^6.4.15" }, "require-dev": { - "illuminate/console": "^10.48.22", - "illuminate/support": "^10.48.22", - "laravel/pint": "^1.18.1", - "pestphp/pest": "^2", + "illuminate/console": "^10.48.24", + "illuminate/support": "^10.48.24", + "laravel/pint": "^1.18.2", + "pestphp/pest": "^2.36.0", "pestphp/pest-plugin-mock": "2.0.0", - "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan": "^1.12.11", "phpstan/phpstan-strict-rules": "^1.6.1", - "symfony/var-dumper": "^6.4.11", + "symfony/var-dumper": "^6.4.15", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -3459,7 +3459,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v1.16.0" + "source": "https://github.com/nunomaduro/termwind/tree/v1.17.0" }, "funding": [ { @@ -3475,7 +3475,7 @@ "type": "github" } ], - "time": "2024-10-15T15:27:12+00:00" + "time": "2024-11-21T10:36:35+00:00" }, { "name": "ovh/ovh", @@ -5599,16 +5599,16 @@ }, { "name": "respect/validation", - "version": "2.3.7", + "version": "2.3.8", "source": { "type": "git", "url": "https://github.com/Respect/Validation.git", - "reference": "967f7b6cc71e3728bb0f766cc1aea0604b2955aa" + "reference": "25ce44c7ee9613d260c7c0e44e27daa2131f383a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Respect/Validation/zipball/967f7b6cc71e3728bb0f766cc1aea0604b2955aa", - "reference": "967f7b6cc71e3728bb0f766cc1aea0604b2955aa", + "url": "https://api.github.com/repos/Respect/Validation/zipball/25ce44c7ee9613d260c7c0e44e27daa2131f383a", + "reference": "25ce44c7ee9613d260c7c0e44e27daa2131f383a", "shasum": "" }, "require": { @@ -5661,9 +5661,9 @@ ], "support": { "issues": "https://github.com/Respect/Validation/issues", - "source": "https://github.com/Respect/Validation/tree/2.3.7" + "source": "https://github.com/Respect/Validation/tree/2.3.8" }, - "time": "2024-04-13T09:45:55+00:00" + "time": "2024-11-26T09:14:36+00:00" }, { "name": "sabre/uri", @@ -9220,16 +9220,16 @@ }, { "name": "voku/portable-ascii", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b56450eed252f6801410d810c8e1727224ae0743" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -9254,7 +9254,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -9266,7 +9266,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -9290,7 +9290,7 @@ "type": "tidelift" } ], - "time": "2022-03-08T17:03:00+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "webmozart/assert", @@ -9946,16 +9946,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.0", + "version": "3.11.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "70c08f8d20c0eb4fe56f26644dd94dae76a7f450" + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/70c08f8d20c0eb4fe56f26644dd94dae76a7f450", - "reference": "70c08f8d20c0eb4fe56f26644dd94dae76a7f450", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", "shasum": "" }, "require": { @@ -10022,7 +10022,7 @@ "type": "open_collective" } ], - "time": "2024-11-12T09:53:29+00:00" + "time": "2024-11-16T12:02:36+00:00" }, { "name": "symfony/config", diff --git a/flexiapi/config/app.php b/flexiapi/config/app.php index 5880265..7a03c3b 100644 --- a/flexiapi/config/app.php +++ b/flexiapi/config/app.php @@ -127,7 +127,7 @@ return [ */ 'url' => env('APP_URL', 'http://localhost'), - + 'root_domain' => env('APP_ROOT_DOMAIN', null), 'asset_url' => env('ASSET_URL', null), /* diff --git a/flexiapi/config/auth.php b/flexiapi/config/auth.php index e0659c6..0573c2d 100644 --- a/flexiapi/config/auth.php +++ b/flexiapi/config/auth.php @@ -66,19 +66,10 @@ return [ */ 'providers' => [ - /*'users' => [ - 'driver' => 'eloquent', - 'model' => App\User::class, - ],*/ 'users' => [ 'driver' => 'eloquent', 'model' => App\Account::class, ], - - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], ], /* diff --git a/flexiapi/database/factories/AccountFactory.php b/flexiapi/database/factories/AccountFactory.php index 577eb00..2070ec0 100644 --- a/flexiapi/database/factories/AccountFactory.php +++ b/flexiapi/database/factories/AccountFactory.php @@ -26,7 +26,7 @@ use Awobaz\Compoships\Database\Eloquent\Factories\ComposhipsFactory; use App\Account; use App\AccountCreationToken; use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController; -use App\SipDomain; +use App\Space; class AccountFactory extends Factory { @@ -35,9 +35,9 @@ class AccountFactory extends Factory public function definition() { - $domain = SipDomain::count() == 0 - ? SipDomain::factory()->create() - : SipDomain::first(); + $domain = Space::count() == 0 + ? Space::factory()->create() + : Space::first(); return [ 'username' => $this->faker->username, @@ -53,6 +53,13 @@ class AccountFactory extends Factory ]; } + public function fromSpace(Space $space) + { + return $this->state(fn (array $attributes) => [ + 'domain' => $space->domain + ]); + } + public function admin() { return $this->state(fn (array $attributes) => [ @@ -63,9 +70,9 @@ class AccountFactory extends Factory public function superAdmin() { return $this->state(function (array $attributes) { - $sipDomain = SipDomain::where('domain', $attributes['domain'])->first(); - $sipDomain->super = true; - $sipDomain->save(); + $space = Space::where('domain', $attributes['domain'])->first(); + $space->super = true; + $space->save(); return [ 'admin' => true, diff --git a/flexiapi/database/factories/SipDomainFactory.php b/flexiapi/database/factories/SpaceFactory.php similarity index 75% rename from flexiapi/database/factories/SipDomainFactory.php rename to flexiapi/database/factories/SpaceFactory.php index 85ab92e..418e167 100644 --- a/flexiapi/database/factories/SipDomainFactory.php +++ b/flexiapi/database/factories/SpaceFactory.php @@ -19,17 +19,19 @@ namespace Database\Factories; -use App\SipDomain; +use App\Space; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; -class SipDomainFactory extends Factory +class SpaceFactory extends Factory { - protected $model = SipDomain::class; + protected $model = Space::class; public function definition() { return [ 'domain' => config('app.sip_domain'), + 'host' => config('app.sip_domain'), ]; } @@ -37,6 +39,14 @@ class SipDomainFactory extends Factory { return $this->state(fn (array $attributes) => [ 'domain' => 'second_' . config('app.sip_domain'), + 'host' => 'second_' . config('app.sip_domain'), + ]); + } + + public function expired() + { + return $this->state(fn (array $attributes) => [ + 'expire_at' => Carbon::today()->toDateTimeString() ]); } } diff --git a/flexiapi/database/migrations/2024_06_11_090306_create_sip_domains_table.php b/flexiapi/database/migrations/2024_06_11_090306_create_sip_domains_table.php index 063ab4b..74a2561 100644 --- a/flexiapi/database/migrations/2024_06_11_090306_create_sip_domains_table.php +++ b/flexiapi/database/migrations/2024_06_11_090306_create_sip_domains_table.php @@ -1,6 +1,6 @@ select('domain')->distinct()->get()->pluck('domain') as $domain) { - $sipDomain = new SipDomain; - $sipDomain->domain = $domain; - $sipDomain->super = env('APP_ADMINS_MANAGE_MULTI_DOMAINS', false); // historical environnement boolean - $sipDomain->save(); + $space = new Space; + $space->domain = $domain; + $space->super = env('APP_ADMINS_MANAGE_MULTI_DOMAINS', false); // historical environnement boolean + $space->save(); } Schema::table('accounts', function (Blueprint $table) { diff --git a/flexiapi/database/migrations/2024_11_18_103038_rename_sip_domains_to_spaces_table.php b/flexiapi/database/migrations/2024_11_18_103038_rename_sip_domains_to_spaces_table.php new file mode 100644 index 0000000..d2c2279 --- /dev/null +++ b/flexiapi/database/migrations/2024_11_18_103038_rename_sip_domains_to_spaces_table.php @@ -0,0 +1,38 @@ +string('host')->unique()->nullable(); + $table->integer('max_accounts')->default(0); + $table->datetime('expire_at')->nullable(); + }); + + DB::statement("update spaces set host = domain"); + + Schema::table('spaces', function (Blueprint $table) { + $table->string('host')->nullable(false)->change(); + }); + + } + + public function down(): void + { + Schema::rename('spaces', 'sip_domains'); + Schema::table('sip_domains', function (Blueprint $table) { + $table->dropColumn('host'); + $table->dropColumn('max_accounts'); + $table->dropColumn('expire_at'); + }); + } +}; diff --git a/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php b/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php index ae9f892..df76ffd 100644 --- a/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php +++ b/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php @@ -3,7 +3,7 @@ namespace Database\Seeders; use App\Account; -use App\SipDomain; +use App\Space; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; @@ -92,9 +92,9 @@ class LiblinphoneTesterAccoutSeeder extends Seeder // Create the domains foreach ($domains as $domain) { - $sipDomain = SipDomain::where('domain', $domain)->firstOrNew(); - $sipDomain->domain = $domain; - $sipDomain->save(); + $space = Space::where('domain', $domain)->firstOrNew(); + $space->domain = $domain; + $space->save(); } // And seed the fresh ones diff --git a/flexiapi/public/css/form.css b/flexiapi/public/css/form.css index 03c8b2a..324900e 100644 --- a/flexiapi/public/css/form.css +++ b/flexiapi/public/css/form.css @@ -7,12 +7,18 @@ border: 1px solid var(--main-5); border-radius: 3rem; font-size: 1.5rem; - line-height: 2rem; - padding: 1rem 2rem; + line-height: 4rem; + padding: 0 2rem; color: white; white-space: nowrap; } +.btn.small { + line-height: 3rem; + padding: 0rem 2rem; + font-size: 1.25rem; +} + p .btn { margin: 0 1rem; } @@ -94,7 +100,6 @@ form.inline { form div { position: relative; - min-height: 4rem; } form div .btn, @@ -202,10 +207,6 @@ form div select { -webkit-appearance: none; } -form div.checkbox { - min-height: 2rem; -} - form div.search:after, form div.select:after { font-family: 'Phosphor'; @@ -228,6 +229,15 @@ form div.search:after { content: "\e30c"; } +form div span.supporting { + line-height: 2rem; + font-size: 1.25rem; + color: var(--grey-4); + display: block; + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + form div input[disabled], form div textarea[disabled] { border-color: var(--grey-4); @@ -323,4 +333,67 @@ form div textarea:invalid:not(:placeholder-shown) { form div input:invalid:not(:placeholder-shown)+label, form div textarea:invalid:not(:placeholder-shown)+label { color: var(--danger-5); +} + +/* Checkbox element */ + +form div.checkbox { + padding: 0.5rem 0; + display: flex; + align-items: center; +} + +form div.checkbox > div { + margin-right: 5rem; +} + +div.checkbox > input[type="checkbox"] { + display: none; + width: 0; + height: 0; +} + +div.checkbox p { + margin-bottom: 0; +} + +div.checkbox::before { + content: ''; + display: block; + position: absolute; + height: 3rem; + width: 5rem; + background-color: var(--grey-3); + box-sizing: border-box; + transition: background-color 0.3s ease; + top: calc(50% - 1.5rem); + right: 0; + border-radius: 4rem; +} + +div.checkbox:has(> input[type="checkbox"]:checked)::before { + background-color: var(--color-green); +} + +div.checkbox > input[type="checkbox"] + label { + display: block; + background-color: white; + width: 2rem; + height: 2rem; + border-radius: 2rem; + top: calc(50% - 1rem); + right: 2.5rem; + position: absolute; + font-size: 2rem; + text-align: center; + line-height: normal; + transition: right 0.3s ease; +} + +div.checkbox:hover > input[type="checkbox"] + label { + cursor: pointer; +} + +div.checkbox > input[type="checkbox"]:checked + label { + right: 0.5rem; } \ No newline at end of file diff --git a/flexiapi/public/css/style.css b/flexiapi/public/css/style.css index ab3d54e..9bb9446 100644 --- a/flexiapi/public/css/style.css +++ b/flexiapi/public/css/style.css @@ -340,28 +340,42 @@ content section { box-sizing: border-box; } -content section.grid { +.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; } +.grid.third { + grid-template-columns: repeat(3, 1fr); +} + @media screen and (max-width: 800px) { - content section.grid { + .grid { display: block; } + + .grid.third { + grid-template-columns: repeat(2, 1fr); + } } -content section.grid header, -content section.grid .large { +.grid header, +.grid .large { grid-column: span 2; } +.grid.third header, +.grid.third .large { + grid-column: span 3; +} + content section header { display: flex; gap: 1rem; align-items: center; margin-bottom: 3rem; + flex-flow: wrap; } content section header p { @@ -381,6 +395,12 @@ content nav+section { margin-bottom: 4rem; } +@media screen and (max-width: 800px) { + content nav+section { + min-width: 100%; + } +} + /** Sidenav **/ content>nav { @@ -417,7 +437,7 @@ 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); + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.2); } content>nav a i { @@ -758,11 +778,16 @@ select.list_toggle { .card { background-color: var(--grey-1); border-radius: 1rem; - padding: 1rem; + padding: 1.5rem; margin-bottom: 1rem; overflow: hidden; } +.card .icon { + font-size: 3rem; + color: var(--second-6); +} + .disabled { opacity: 0.5; pointer-events: none; @@ -805,6 +830,28 @@ select.list_toggle { display: inline-block; } +/* Progress */ + +progress { + width: 100%; + border-radius: 2rem; +} + +progress::-webkit-progress-bar, +progress::-moz-progress-bar { + background-color: var(--color-green); +} + +progress.yellow::-webkit-progress-bar, +progress.yellow::-moz-progress-bar { + background-color: var(--color-yellow); +} + +progress.orange::-webkit-progress-bar, +progress.orange::-moz-progress-bar { + background-color: var(--color-orange); +} + /* Steps */ ol.steps { diff --git a/flexiapi/public/scripts/utils.js b/flexiapi/public/scripts/utils.js index 1cd117f..dafb437 100644 --- a/flexiapi/public/scripts/utils.js +++ b/flexiapi/public/scripts/utils.js @@ -114,4 +114,16 @@ function digitFilled(element) { if (element.value.length == 1) { element.nextElementSibling.focus(); } +} + +function copyValueTo(from, to, append) { + if (to.value == '') { + let value = from.value; + + if (append) { + value += append; + } + + to.value = value; + } } \ No newline at end of file diff --git a/flexiapi/resources/views/account/dashboard.blade.php b/flexiapi/resources/views/account/dashboard.blade.php index b2daf0b..4937f39 100644 --- a/flexiapi/resources/views/account/dashboard.blade.php +++ b/flexiapi/resources/views/account/dashboard.blade.php @@ -2,7 +2,7 @@ @section('content')
-

gauge Dashboard

+

gauge My Account

@@ -64,7 +64,7 @@

envelope SIP address: sip:{{ $account->identifier }}

user Username: {{ $account->username }}

-

hard-drives Domain: {{ $account->domain }}

+

globe-hemisphere-west Domain: {{ $account->domain }}

@if (!empty(config('app.proxy_registrar_address')))

lan Proxy/registrar address: sip:{{ config('app.proxy_registrar_address') }} diff --git a/flexiapi/resources/views/admin/account/create_edit.blade.php b/flexiapi/resources/views/admin/account/create_edit.blade.php index 67b960c..af16bf5 100644 --- a/flexiapi/resources/views/admin/account/create_edit.blade.php +++ b/flexiapi/resources/views/admin/account/create_edit.blade.php @@ -48,9 +48,9 @@

diff --git a/flexiapi/resources/views/admin/account/index.blade.php b/flexiapi/resources/views/admin/account/index.blade.php index 61c8617..06b6ad2 100644 --- a/flexiapi/resources/views/admin/account/index.blade.php +++ b/flexiapi/resources/views/admin/account/index.blade.php @@ -9,6 +9,9 @@ @section('content')

users Accounts

+ @if ($space) +

{{ $accounts->count()}} / @if ($space->max_accounts > 0){{ $space->max_accounts }} @else infinity@endif

+ @endif download-simple Import Accounts diff --git a/flexiapi/resources/views/admin/sip_domain/create_edit.blade.php b/flexiapi/resources/views/admin/sip_domain/create_edit.blade.php deleted file mode 100644 index 3ac9a89..0000000 --- a/flexiapi/resources/views/admin/sip_domain/create_edit.blade.php +++ /dev/null @@ -1,73 +0,0 @@ -@extends('layouts.main') - -@section('breadcrumb') - - -@endsection - -@section('content') -
- @if ($sip_domain->id) -

hard-drives {{ $sip_domain->domain }}

- Cancel - - trash - Delete - - - @else -

user-rectangle Create a SIP Domain

- Cancel - - @endif -
- -
- @csrf - @method($sip_domain->id ? 'put' : 'post') - @if (!$sip_domain->id) -
- - - @include('parts.errors', ['name' => 'domain']) -
- @endif - - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'super', 'label' => 'Super domain']) - -

Features

- - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'disable_chat_feature', 'label' => 'Chat feature', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'disable_meetings_feature', 'label' => 'Meeting feature', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'disable_broadcast_feature', 'label' => 'Conference feature', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'hide_settings', 'label' => 'General settings', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'hide_account_settings', 'label' => 'Account settings', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'disable_call_recordings_feature', 'label' => 'Record audio/video calls', 'reverse' => true]) - -

General toggles

- - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'only_display_sip_uri_username', 'label' => 'Only display usernames (hide SIP addresses)']) - -
- - -
- -

Assistant

- - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'assistant_hide_create_account', 'label' => 'Account creation panel', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'assistant_disable_qr_code', 'label' => 'QR Code scanning panel', 'reverse' => true]) - @include('parts.form.toggle', ['object' => $sip_domain, 'key' => 'assistant_hide_third_party_account', 'label' => 'Third party SIP panel', 'reverse' => true]) - -
-@endsection diff --git a/flexiapi/resources/views/admin/sip_domain/index.blade.php b/flexiapi/resources/views/admin/sip_domain/index.blade.php deleted file mode 100644 index d6cf37a..0000000 --- a/flexiapi/resources/views/admin/sip_domain/index.blade.php +++ /dev/null @@ -1,43 +0,0 @@ -@extends('layouts.main') - -@section('breadcrumb') - -@endsection - -@section('content') - -
-

hard-drives SIP Domains

- - plus - New SIP Domain - -
- - - - - - - - - - @foreach ($sip_domains as $sip_domain) - - - - - @endforeach - -
SIP DomainAccounts
- - {{ $sip_domain->domain }} - @if ($sip_domain->super) Super @endif - - - {{ $sip_domain->accounts_count }} -
- -@endsection \ No newline at end of file diff --git a/flexiapi/resources/views/admin/space/create.blade.php b/flexiapi/resources/views/admin/space/create.blade.php new file mode 100644 index 0000000..750041c --- /dev/null +++ b/flexiapi/resources/views/admin/space/create.blade.php @@ -0,0 +1,46 @@ +@extends('layouts.main') + +@section('breadcrumb') + + +@endsection + +@section('content') +
+

user-rectangle Create a Space

+ Cancel +
+ +
+ @csrf + @method('post') + +
+ + + + @include('parts.errors', ['name' => 'host']) + @include('parts.errors', ['name' => 'full_host']) + Cannot be changed once created +
+ +
+ + + @include('parts.errors', ['name' => 'domain']) + Cannot be changed once created +
+ + @include('parts.form.toggle', ['object' => $space, 'key' => 'super', 'label' => 'Super space', 'supporting' => 'All the admins in this Space will be Super Admins']) + +
+ +
+ +
+@endsection diff --git a/flexiapi/resources/views/admin/sip_domain/delete.blade.php b/flexiapi/resources/views/admin/space/delete.blade.php similarity index 50% rename from flexiapi/resources/views/admin/sip_domain/delete.blade.php rename to flexiapi/resources/views/admin/space/delete.blade.php index 0906c8c..6d4cb8a 100644 --- a/flexiapi/resources/views/admin/sip_domain/delete.blade.php +++ b/flexiapi/resources/views/admin/space/delete.blade.php @@ -2,27 +2,26 @@ @section('breadcrumb') @endsection @section('content')
-

trash Delete a SIP Domain

- Cancel - +

trash Delete a Space

+ Cancel
-
+ @csrf @method('delete')

You are going to permanently delete the following domain please confirm your action.

-

{{ $sip_domain->domain }}

-

This will also destroy {{ $sip_domain->accounts()->count() }} related accounts

+

{{ $space->domain }}

+

This will also destroy {{ $space->accounts()->count() }} related accounts

- +
@@ -30,5 +29,9 @@ @include('parts.errors', ['name' => 'domain'])
+ +
+ +
@endsection diff --git a/flexiapi/resources/views/admin/space/edit.blade.php b/flexiapi/resources/views/admin/space/edit.blade.php new file mode 100644 index 0000000..6608b2c --- /dev/null +++ b/flexiapi/resources/views/admin/space/edit.blade.php @@ -0,0 +1,57 @@ +@extends('layouts.main') + +@section('breadcrumb') + + + +@endsection + +@section('content') +
+

globe-hemisphere-west {{ $space->host }}

+
+ + @include('admin.space.tabs') + +
+ @csrf + @method('put') + +

Features

+ + @include('parts.form.toggle', ['object' => $space, 'key' => 'disable_chat_feature', 'label' => 'Chat feature', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'disable_meetings_feature', 'label' => 'Meeting feature', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'disable_broadcast_feature', 'label' => 'Conference feature', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'hide_settings', 'label' => 'General settings', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'hide_account_settings', 'label' => 'Account settings', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'disable_call_recordings_feature', 'label' => 'Record audio/video calls', 'reverse' => true]) + +

General toggles

+ + @include('parts.form.toggle', ['object' => $space, 'key' => 'only_display_sip_uri_username', 'label' => 'Only display usernames (hide SIP addresses)']) + +
+ + +
+ +

Assistant

+ + @include('parts.form.toggle', ['object' => $space, 'key' => 'assistant_hide_create_account', 'label' => 'Account creation panel', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'assistant_disable_qr_code', 'label' => 'QR Code scanning panel', 'reverse' => true]) + @include('parts.form.toggle', ['object' => $space, 'key' => 'assistant_hide_third_party_account', 'label' => 'Third party SIP panel', 'reverse' => true]) + +
+ +
+
+@endsection diff --git a/flexiapi/resources/views/admin/space/index.blade.php b/flexiapi/resources/views/admin/space/index.blade.php new file mode 100644 index 0000000..6fc849b --- /dev/null +++ b/flexiapi/resources/views/admin/space/index.blade.php @@ -0,0 +1,55 @@ +@extends('layouts.main') + +@section('breadcrumb') + +@endsection + +@section('content') + +
+

globe-hemisphere-west Spaces

+ + plus + New Space + +
+ + + + + + + + + + + + @foreach ($spaces as $space) + + + + + + + @endforeach + +
SpaceSIP DomainAccountsExpiration
+ + {{ $space->host }} + @if ($space->super) Super @endif + + {{ $space->domain }} + {{ $space->accounts_count }} / @if ($space->max_accounts > 0){{ $space->max_accounts }} @else infinity@endif + + @if ($space->isExpired()) + Expired + @elseif ($space->expire_at) + In {{ $space->daysLeft }} days + @else + infinity + @endif +
+ +@endsection \ No newline at end of file diff --git a/flexiapi/resources/views/admin/space/parameters.blade.php b/flexiapi/resources/views/admin/space/parameters.blade.php new file mode 100644 index 0000000..7d81787 --- /dev/null +++ b/flexiapi/resources/views/admin/space/parameters.blade.php @@ -0,0 +1,45 @@ +@extends('layouts.main') + +@section('breadcrumb') + + + +@endsection + +@section('content') +
+

globe-hemisphere-west {{ $space->host }}

+
+ + @include('admin.space.tabs') + +
+ @csrf + @method('put') + +
+ + + Unlimited if set to 0 + @include('parts.errors', ['name' => 'max_accounts']) +
+ +
+ expire_at) value="{{ $space->expire_at->toDateString() }}" @endif min="{{ \Carbon\Carbon::now()->toDateString() }}"> + + Clear to never expire +
+ +
+ @include('parts.form.toggle', ['object' => $space, 'key' => 'super', 'label' => 'Super space', 'supporting' => 'All the admins in this Space will be Super Admins']) +
+ +
+ +
+
+@endsection diff --git a/flexiapi/resources/views/admin/space/show.blade.php b/flexiapi/resources/views/admin/space/show.blade.php new file mode 100644 index 0000000..733ca0c --- /dev/null +++ b/flexiapi/resources/views/admin/space/show.blade.php @@ -0,0 +1,82 @@ +@extends('layouts.main') + +@section('breadcrumb') + + + +@endsection + +@section('content') +
+

globe-hemisphere-west {{ $space->host }}

+ + trash + Delete + + + isFull())disabled @endif href="{{ route('admin.account.create', ['domain' => $space->domain]) }}"> + user-plus New Account + +
+ + @include('admin.space.tabs') + +
+
+ users +

Accounts

+ @if ($space->max_accounts > 0) + + @endif +

+ {{ $space->accounts()->count() }} + / + @if ($space->max_accounts > 0){{ $space->max_accounts }} @else infinity@endif +

+
+
+ clock +

Expiration

+ @if ($space->isExpired()) +

Expired

+ @elseif ($space->expire_at) +

In {{ $space->daysLeft }} days ({{ $space->expire_at->toDateString() }})

+ @else +

Never expire

+ @endif +
+
+ + isFull())disabled @endif href="{{ route('admin.account.create', ['admin' => true, 'domain' => $space->domain]) }}">user-plus New Admin + +

Admins

+ + + + + @include('parts.column_sort', ['uriParams' => ['space' => $space], 'key' => 'username', 'title' => 'Identifier']) + @include('parts.column_sort', ['uriParams' => ['space' => $space], 'key' => 'updated_at', 'title' => 'Updated']) + + + + @if ($space->admins->isEmpty()) + + + + @endif + @foreach ($space->admins as $admin) + + + + + @endforeach + +
No Admins
+ + {{ $admin->identifier }} + + {{ $admin->updated_at }}
+@endsection diff --git a/flexiapi/resources/views/admin/space/tabs.blade.php b/flexiapi/resources/views/admin/space/tabs.blade.php new file mode 100644 index 0000000..6a68a67 --- /dev/null +++ b/flexiapi/resources/views/admin/space/tabs.blade.php @@ -0,0 +1,14 @@ +@php + $items = [ + route('admin.spaces.show', $space->id) => 'Information', + route('admin.spaces.edit', $space->id) => 'Configuration' + ]; + + if (auth()->user()->superAdmin) { + $items[route('admin.spaces.parameters', $space->id)] = 'Parameters'; + } +@endphp + +@include('parts.tabs', [ + 'items' => $items +]) \ No newline at end of file diff --git a/flexiapi/resources/views/api/documentation_markdown.blade.php b/flexiapi/resources/views/api/documentation_markdown.blade.php index f6e01f2..da3e8e1 100644 --- a/flexiapi/resources/views/api/documentation_markdown.blade.php +++ b/flexiapi/resources/views/api/documentation_markdown.blade.php @@ -26,6 +26,10 @@ The endpoints are accessible using three different models: - Admin the endpoint can be only be accessed by an authenticated admin user - Super Admin the endpoint can be only be accessed by an authenticated super admin user +### Space expiration + +Super Admin can configure and expiration date on Spaces (`expire_at`). If the Space is expired all the authenticated endpoint of the API will return `403`. + ### Localization You can add an [`Accept-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) header to your request to translate the responses, and especially errors messages, in a specific language. @@ -128,28 +132,28 @@ An `account_creation_request_token` is a unique token that can be validated and Create and return an `account_creation_request_token` that should then be validated to be used. -## SIP Domains +## Spaces -Manage the list of allowed `sip_domains`. The admin accounts declared with a `domain` that is a `super` `sip_domain` will become Super Admin. +Manage the list of allowed `spaces`. The admin accounts declared with a `domain` that is a `super` `sip_domain` will become Super Admin. -### `GET /sip_domains` +### `GET /spaces` Super Admin -Get the list of declared SIP Domains. +Get the list of declared Spaces. -### `GET /sip_domains/{domain}` +### `GET /spaces/{domain}` Super Admin -Get a SIP Domain. +Get a Space. -### `POST /sip_domains` +### `POST /spaces` Super Admin Create a new `sip_domain`. JSON parameters: -* `domain` **required**, the domain to use, must be unique +* `domain` **required**, the SIP domain to use, must be unique * `super` **required**, boolean, set the domain as a Super Domain * `disable_chat_feature` boolean, disable the chat feature, default to `false` * `disable_meetings_feature` boolean, disable the meeting feature, default to `false` @@ -162,8 +166,10 @@ JSON parameters: * `assistant_disable_qr_code` boolean, disable the QR code feature in the assistant, default to `false` * `assistant_hide_third_party_account` boolean, disable the call recording feature, default to `false` * `max_account` integer, the maximum number of accounts configurable in the app, default to `0` (infinite) +* `max_accounts` integer, the maximum number of accounts that can be created in the space, default to `0` (infinite), cannot be less than the actual amount of accounts +* `expire_at` date, the moment the space is expiring, default to `null` (never expire) -### `PUT /sip_domains/{domain}` +### `PUT /spaces/{domain}` Super Admin Update an existing `sip_domain`. @@ -182,8 +188,10 @@ JSON parameters: * `assistant_disable_qr_code` **required**, boolean * `assistant_hide_third_party_account` **required**, boolean * `max_account` **required**, integer +* `max_accounts` **required**,integer, the maximum number of accounts that can be created in the space, default to `0` (infinite), cannot be less than the actual amount of accounts +* `expire_at` **required**, date, the moment the space is expiring, set to `null` to never expire -### `DELETE /sip_domains/{domain}` +### `DELETE /spaces/{domain}` Super Admin Delete a domain, **be careful, all the related accounts will also be destroyed**. @@ -272,8 +280,11 @@ JSON parameters: Public Create an account using an `account_creation_token`. + Return `422` if the parameters are invalid or if the token is expired. +Return `403` if the `max_accounts` limit of the corresponding Space is reached. + JSON parameters: * `username` unique username, minimum 6 characters @@ -397,12 +408,14 @@ JSON parameters: To create an account directly from the API. Deprecated If `activated` is set to `false` a random generated `confirmation_key` and `provisioning_token` will be returned to allow further activation using the public endpoints and provision the account. Check `confirmation_key_expires` to also set an expiration date on that `confirmation_key`. +Return `403` if the `max_accounts` limit of the corresponding Space is reached. + JSON parameters: * `username` unique username, minimum 6 characters * `password` **required** minimum 6 characters * `algorithm` **required**, values can be `SHA-256` or `MD5` -* `domain` **not configurable by default**. Only configurable if the admin is a super admin. Otherwise `APP_SIP_DOMAIN` is used. If the domain is not available in the `sip_domains` list, it will be created automatically. +* `domain` **not configurable by default**, must exist in one of the configured Spaces. Only configurable if the admin is a super admin. Otherwise the SIP domain of the corresponding space is used. * `activated` optional, a boolean, set to `false` by default * `display_name` optional, string * `email` optional, must be an email, must be unique if `ACCOUNT_EMAIL_UNIQUE` is set to `true` @@ -420,7 +433,7 @@ Update an existing account. Ensure to resend all the parameters to not reset the JSON parameters: * `username` unique username, minimum 6 characters -* `domain` **not configurable by default**. Only configurable if the admin is a super admin. Otherwise `APP_SIP_DOMAIN` is used. +* `domain` **not configurable by default**, must exist in one of the configured Spaces. Only configurable if the admin is a super admin. Otherwise the SIP domain of the corresponding space is used. * `password` **required** minimum 6 characters * `algorithm` **required**, values can be `SHA-256` or `MD5` * `display_name` optional, string diff --git a/flexiapi/resources/views/errors/402.blade.php b/flexiapi/resources/views/errors/402.blade.php new file mode 100644 index 0000000..3bc23ef --- /dev/null +++ b/flexiapi/resources/views/errors/402.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Payment Required')) +@section('code', '402') +@section('message', __('Payment Required')) diff --git a/flexiapi/resources/views/errors/503.blade.php b/flexiapi/resources/views/errors/503.blade.php index c5a9dde..3d72dbc 100644 --- a/flexiapi/resources/views/errors/503.blade.php +++ b/flexiapi/resources/views/errors/503.blade.php @@ -2,4 +2,4 @@ @section('title', __('Service Unavailable')) @section('code', '503') -@section('message', __('Service Unavailable')) +@section('message', $exception->getMessage()) diff --git a/flexiapi/resources/views/errors/illustrated-layout.blade.php b/flexiapi/resources/views/errors/illustrated-layout.blade.php deleted file mode 100644 index 2e5b824..0000000 --- a/flexiapi/resources/views/errors/illustrated-layout.blade.php +++ /dev/null @@ -1,486 +0,0 @@ - - - - - - - @yield('title') - - - - - - - - - -
-
-
-
- @yield('code', __('Oh no')) -
- -
- -

- @yield('message') -

- - - - -
-
- -
- @yield('image') -
-
- - diff --git a/flexiapi/resources/views/errors/layout.blade.php b/flexiapi/resources/views/errors/layout.blade.php deleted file mode 100644 index 4f2318f..0000000 --- a/flexiapi/resources/views/errors/layout.blade.php +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - @yield('title') - - - - - - - - - -
-
-
- @yield('message') -
-
-
- - diff --git a/flexiapi/resources/views/errors/minimal.blade.php b/flexiapi/resources/views/errors/minimal.blade.php index be74c4b..e87611c 100644 --- a/flexiapi/resources/views/errors/minimal.blade.php +++ b/flexiapi/resources/views/errors/minimal.blade.php @@ -2,7 +2,7 @@ @section('content') -

@yield('code') - @yield('title')

+

@yield('code') - @yield('title')

@yield('message') diff --git a/flexiapi/resources/views/layouts/main.blade.php b/flexiapi/resources/views/layouts/main.blade.php index 720feda..11b23fb 100644 --- a/flexiapi/resources/views/layouts/main.blade.php +++ b/flexiapi/resources/views/layouts/main.blade.php @@ -13,7 +13,7 @@ @endif - + @@ -63,7 +63,7 @@ @hasSection('breadcrumb')

diff --git a/flexiapi/resources/views/parts/errors.blade.php b/flexiapi/resources/views/parts/errors.blade.php index bd74b7c..bd4e35b 100644 --- a/flexiapi/resources/views/parts/errors.blade.php +++ b/flexiapi/resources/views/parts/errors.blade.php @@ -1,3 +1,4 @@ + @if (isset($errors) && isset($name) && count($errors->get($name)) > 0) @foreach ($errors->get($name) as $error) diff --git a/flexiapi/resources/views/parts/form/toggle.blade.php b/flexiapi/resources/views/parts/form/toggle.blade.php index 14f2c83..f288970 100644 --- a/flexiapi/resources/views/parts/form/toggle.blade.php +++ b/flexiapi/resources/views/parts/form/toggle.blade.php @@ -1,13 +1,10 @@ -
- @if (!isset($reverse) || !$reverse) - $key) checked @endif> - @if (isset($reverse) && $reverse)

Disabled

@else

Enabled

@endif - @endif - $key) checked @endif> - @if (isset($reverse) && $reverse)

Enabled

@else

Disabled

@endif - - @if (isset($reverse) && $reverse) - $key) checked @endif> - @if (isset($reverse) && $reverse)

Disabled

@else

Enabled

@endif - @endif +
+ $key || (isset($reversed) && $reversed && !$object->$key)) checked @endif name="{{ $key }}"> + +
+

{{ $label }}

+ @if (isset($supporting)) + {{ $supporting }} + @endif +
\ No newline at end of file diff --git a/flexiapi/resources/views/parts/sidebar.blade.php b/flexiapi/resources/views/parts/sidebar.blade.php index 0fa0fbd..f7df1d5 100644 --- a/flexiapi/resources/views/parts/sidebar.blade.php +++ b/flexiapi/resources/views/parts/sidebar.blade.php @@ -1,18 +1,18 @@