From 672d6291b71d0c309e7dabc6d7be746bd33343f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Jaussoin?= Date: Tue, 8 Apr 2025 09:02:35 +0000 Subject: [PATCH] Fix FLEXIAPI-272 Add Space based email server integration --- CHANGELOG.md | 1 + flexiapi/app/ExternalAccount.php | 16 ++++ .../Admin/Space/EmailServerController.php | 60 ++++++++++++++ .../Controllers/Admin/SpaceController.php | 7 ++ .../Api/Admin/EmailServerController.php | 43 ++++++++++ flexiapi/app/Http/Kernel.php | 2 +- .../{IsSpaceExpired.php => SpaceCheck.php} | 21 ++++- .../Requests/EmailServer/CreateUpdate.php | 38 +++++++++ flexiapi/app/PhoneCountry.php | 16 ++++ flexiapi/app/ResetPasswordEmailToken.php | 16 ++++ flexiapi/app/Space.php | 23 ++++++ flexiapi/app/SpaceEmailServer.php | 34 ++++++++ ...95857_create_space_email_servers_table.php | 36 +++++++++ flexiapi/lang/fr.json | 2 + flexiapi/public/css/form.css | 4 + flexiapi/public/css/style.css | 36 ++++++++- .../views/account/documentation.blade.php | 8 +- .../admin/space/email_server/delete.blade.php | 38 +++++++++ .../admin/space/email_server/show.blade.php | 79 +++++++++++++++++++ .../views/admin/space/integration.blade.php | 39 +++++++++ .../views/admin/space/tabs.blade.php | 2 + .../views/api/documentation.blade.php | 8 +- .../api/documentation_markdown.blade.php | 31 +++++++- .../provisioning/documentation.blade.php | 8 +- flexiapi/routes/api.php | 9 ++- flexiapi/routes/web.php | 23 ++++-- .../tests/Feature/ApiSpaceEmailServerTest.php | 77 ++++++++++++++++++ flexiapi/tests/TestCase.php | 4 +- 28 files changed, 652 insertions(+), 29 deletions(-) create mode 100644 flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php create mode 100644 flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php rename flexiapi/app/Http/Middleware/{IsSpaceExpired.php => SpaceCheck.php} (53%) create mode 100644 flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php create mode 100644 flexiapi/app/SpaceEmailServer.php create mode 100644 flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php create mode 100644 flexiapi/resources/views/admin/space/email_server/delete.blade.php create mode 100644 flexiapi/resources/views/admin/space/email_server/show.blade.php create mode 100644 flexiapi/resources/views/admin/space/integration.blade.php create mode 100644 flexiapi/tests/Feature/ApiSpaceEmailServerTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f02d5..6282944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ v1.7 - Fix FLEXIAPI-278 Complete and reorganize the Markdown documentation - Fix FLEXIAPI-233 Add External Accounts (new version) - Fix FLEXIAPI-277 Restrict authorized ini keys that can be set to prevent conflict with the existing ones set in the UI +- Fix FLEXIAPI-272 Add Space based email server integration v1.6 ---- diff --git a/flexiapi/app/ExternalAccount.php b/flexiapi/app/ExternalAccount.php index 453f20c..08600d7 100644 --- a/flexiapi/app/ExternalAccount.php +++ b/flexiapi/app/ExternalAccount.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php b/flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php new file mode 100644 index 0000000..3abed1a --- /dev/null +++ b/flexiapi/app/Http/Controllers/Admin/Space/EmailServerController.php @@ -0,0 +1,60 @@ + $space, + 'emailServer' => $space->emailServer ?? new SpaceEmailServer + ]); + } + + public function store(CreateUpdate $request, int $spaceId) + { + $space = Space::findOrFail($spaceId); + $emailServer = $space->emailServer ?? new SpaceEmailServer; + + $emailServer->space_id = $space->id; + $emailServer->host = $request->get('host'); + $emailServer->port = $request->get('port'); + $emailServer->username = $request->get('username'); + $emailServer->password = $request->get('password'); + $emailServer->from_address = $request->get('from_address') ?? null; + $emailServer->from_name = $request->get('from_name') ?? null; + $emailServer->signature = $request->get('signature') ?? null; + + $emailServer->save(); + + return redirect()->route('admin.spaces.integration', $spaceId); + } + + public function delete(int $spaceId) + { + $space = Space::findOrFail($spaceId); + + return view('admin.space.email_server.delete', [ + 'space' => $space + ]); + } + + public function destroy(int $spaceId) + { + $space = Space::findOrFail($spaceId); + $space->emailServer->delete(); + + return redirect()->route('admin.spaces.integration', $spaceId); + } +} diff --git a/flexiapi/app/Http/Controllers/Admin/SpaceController.php b/flexiapi/app/Http/Controllers/Admin/SpaceController.php index deaa90c..47f7881 100644 --- a/flexiapi/app/Http/Controllers/Admin/SpaceController.php +++ b/flexiapi/app/Http/Controllers/Admin/SpaceController.php @@ -104,6 +104,13 @@ class SpaceController extends Controller ]); } + public function integration(Space $space) + { + return view('admin.space.integration', [ + 'space' => $space + ]); + } + public function configurationUpdate(Request $request, Space $space) { $space = $this->setConfiguration($request, $space); diff --git a/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php b/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php new file mode 100644 index 0000000..11f9f2f --- /dev/null +++ b/flexiapi/app/Http/Controllers/Api/Admin/EmailServerController.php @@ -0,0 +1,43 @@ +firstOrFail()->emailServer()->firstOrFail(); + } + + public function store(CreateUpdate $request, string $host) + { + $space = Space::where('host', $host)->firstOrFail(); + $emailServer = $space->emailServer ?? new SpaceEmailServer; + + $emailServer->space_id = $space->id; + $emailServer->host = $request->get('host'); + $emailServer->port = $request->get('port'); + $emailServer->username = $request->get('username'); + $emailServer->password = $request->get('password'); + $emailServer->from_address = $request->get('from_address') ?? null; + $emailServer->from_name = $request->get('from_name') ?? null; + $emailServer->signature = $request->get('signature') ?? null; + + $emailServer->save(); + + return $emailServer; + } + + public function destroy(string $host) + { + $space = Space::where('host', $host)->firstOrFail(); + return $space->emailServer->delete(); + } +} diff --git a/flexiapi/app/Http/Kernel.php b/flexiapi/app/Http/Kernel.php index 67d41cf..8bee932 100644 --- a/flexiapi/app/Http/Kernel.php +++ b/flexiapi/app/Http/Kernel.php @@ -90,7 +90,7 @@ class Kernel extends HttpKernel 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, - 'space.expired' => \App\Http\Middleware\IsSpaceExpired::class, + 'space.check' => \App\Http\Middleware\SpaceCheck::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/SpaceCheck.php similarity index 53% rename from flexiapi/app/Http/Middleware/IsSpaceExpired.php rename to flexiapi/app/Http/Middleware/SpaceCheck.php index dbe0936..6f3e3a9 100644 --- a/flexiapi/app/Http/Middleware/IsSpaceExpired.php +++ b/flexiapi/app/Http/Middleware/SpaceCheck.php @@ -7,7 +7,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Config; use Symfony\Component\HttpFoundation\Response; -class IsSpaceExpired +class SpaceCheck { public function handle(Request $request, Closure $next): Response { @@ -29,6 +29,25 @@ class IsSpaceExpired abort(403, 'The related Space has expired'); } + // Custom email integration + if ($space->emailServer) { + $config = [ + 'driver' => config('mail.driver'), + 'encryption' => config('mail.encryption'), + 'host' => $space->emailServer->host, + 'port' => $space->emailServer->port, + 'from' => [ + 'address' => $space->emailServer->from_address, + 'name' => $space->emailServer->from_name + ], + 'username' => $space->emailServer->username, + 'password' => $space->emailServer->password, + 'signature' => $space->emailServer->signature ?? config('mail.signature') + ]; + + Config::set('mail', $config); + } + return $next($request); } diff --git a/flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php b/flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php new file mode 100644 index 0000000..ce34ced --- /dev/null +++ b/flexiapi/app/Http/Requests/EmailServer/CreateUpdate.php @@ -0,0 +1,38 @@ +. +*/ + +namespace App\Http\Requests\EmailServer; + +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rule; + +use App\EmailServer; +use App\Rules\Domain; + +class CreateUpdate extends FormRequest +{ + public function rules() + { + return [ + 'host' => ['required', new Domain()], + 'port' => 'required|integer', + 'from_address' => 'nullable|email', + ]; + } +} diff --git a/flexiapi/app/PhoneCountry.php b/flexiapi/app/PhoneCountry.php index aa51092..ed18a0d 100644 --- a/flexiapi/app/PhoneCountry.php +++ b/flexiapi/app/PhoneCountry.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/flexiapi/app/ResetPasswordEmailToken.php b/flexiapi/app/ResetPasswordEmailToken.php index 10241d9..ecb1181 100644 --- a/flexiapi/app/ResetPasswordEmailToken.php +++ b/flexiapi/app/ResetPasswordEmailToken.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; diff --git a/flexiapi/app/Space.php b/flexiapi/app/Space.php index 93463e7..b1ba02a 100644 --- a/flexiapi/app/Space.php +++ b/flexiapi/app/Space.php @@ -1,5 +1,21 @@ . +*/ namespace App; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -11,6 +27,8 @@ class Space extends Model { use HasFactory; + protected $with = ['emailServer']; + public const FORBIDDEN_KEYS = [ 'disable_chat_feature', 'disable_meetings_feature', @@ -60,6 +78,11 @@ class Space extends Model return $this->accounts()->where('admin', true); } + public function emailServer() + { + return $this->hasOne(SpaceEmailServer::class); + } + public function scopeNotFull(Builder $query) { return $query->where('max_accounts', 0) diff --git a/flexiapi/app/SpaceEmailServer.php b/flexiapi/app/SpaceEmailServer.php new file mode 100644 index 0000000..e31668d --- /dev/null +++ b/flexiapi/app/SpaceEmailServer.php @@ -0,0 +1,34 @@ +. +*/ +namespace App; + +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; + +class SpaceEmailServer extends Model +{ + use HasFactory; + + protected $hidden = ['space_id']; + + public function space() + { + return $this->belongsTo(Space::class); + } +} diff --git a/flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php b/flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php new file mode 100644 index 0000000..f2e4aee --- /dev/null +++ b/flexiapi/database/migrations/2025_04_02_095857_create_space_email_servers_table.php @@ -0,0 +1,36 @@ +id(); + + $table->string('host', 64); + $table->integer('port'); + $table->string('username', 128)->nullable(); + $table->string('password', 128)->nullable(); + $table->string('from_address', 128)->nullable(); + $table->string('from_name', 128)->nullable(); + $table->string('signature', 256)->nullable(); + + $table->bigInteger('space_id')->unsigned(); + $table->foreign('space_id')->references('id') + ->on('spaces')->onDelete('cascade'); + + $table->unique('space_id'); + + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('space_email_servers'); + } +}; diff --git a/flexiapi/lang/fr.json b/flexiapi/lang/fr.json index c7ba0d9..f85f6d0 100644 --- a/flexiapi/lang/fr.json +++ b/flexiapi/lang/fr.json @@ -63,6 +63,7 @@ "Edit": "Éditer", "Email registration": "Inscription par email", "Email": "Email", + "Email Server": "Serveur Mail", "Empty": "Vide", "Enable the web interface": "Activer l'interface web", "Enabled": "Activé", @@ -89,6 +90,7 @@ "In ini format, will complete the other settings": "Au format ini, complètera les autres paramètres", "In lowercase letters": "En minuscules", "Information": "Informations", + "Integration": "Intégration", "Intercom features": "Fonctionnalités d'interphonie", "It might actually disable this page, be careful": "Cette page pourrait être désactivée, faites attention", "Key": "Clef", diff --git a/flexiapi/public/css/form.css b/flexiapi/public/css/form.css index 6d1dcef..2297532 100644 --- a/flexiapi/public/css/form.css +++ b/flexiapi/public/css/form.css @@ -35,6 +35,10 @@ p .btn { text-align: right; } +p .btn.oppose { + margin-right: 0; +} + .btn[disabled] { color: var(--main-5); border-color: var(--main-5); diff --git a/flexiapi/public/css/style.css b/flexiapi/public/css/style.css index 5c230e4..f850e34 100644 --- a/flexiapi/public/css/style.css +++ b/flexiapi/public/css/style.css @@ -342,6 +342,23 @@ content section { box-sizing: border-box; } +content:has(section.documentation) { + margin: 0; +} + +content section.documentation { + max-width: calc(100% - 35rem); + margin: 0; + margin-left: 35rem; +} + +@media screen and (max-width: 800px) { + content section.documentation { + max-width: 100%; + margin-left: 0; + } +} + form section { margin: initial; max-width: initial; @@ -787,14 +804,29 @@ select.list_toggle { /** Specific elements */ .table-of-contents { - max-width: 40%; - float: right; + max-width: 33rem; + max-height: calc(100vh - 19rem); + overflow-y: scroll; + position: fixed; + top: 11rem; + left: 0; + padding: 2rem; + box-sizing: border-box; + background-color: var(--grey-1); + border-radius: 0 1rem 1rem 0; +} + +@media screen and (max-width: 800px) { + .table-of-contents { + display: none; + } } .card { background-color: var(--grey-1); border-radius: 1rem; padding: 1.5rem; + box-sizing: border-box; margin-bottom: 1rem; overflow: hidden; } diff --git a/flexiapi/resources/views/account/documentation.blade.php b/flexiapi/resources/views/account/documentation.blade.php index 3f0ee58..4434565 100644 --- a/flexiapi/resources/views/account/documentation.blade.php +++ b/flexiapi/resources/views/account/documentation.blade.php @@ -1,8 +1,8 @@ @extends('layouts.main', ['welcome' => true]) @section('content') -
- {{-- This view is only a wrapper for the markdown page --}} - {!! $documentation !!} -
+
+ {{-- This view is only a wrapper for the markdown page --}} + {!! $documentation !!} +
@endsection \ No newline at end of file diff --git a/flexiapi/resources/views/admin/space/email_server/delete.blade.php b/flexiapi/resources/views/admin/space/email_server/delete.blade.php new file mode 100644 index 0000000..baedbf8 --- /dev/null +++ b/flexiapi/resources/views/admin/space/email_server/delete.blade.php @@ -0,0 +1,38 @@ +@extends('layouts.main') + +@section('breadcrumb') + + + + +@endsection + +@section('content') +
+

trash {{ __('Delete') }}

+ + {{ __('Cancel') }} + +
+
+ @csrf + @method('delete') + +
+

{{ __('You are going to permanently delete the following element. Please confirm your action.') }}
+ envelope {{ $space->emailServer->host }} +

+ +
+
+
+
+@endsection diff --git a/flexiapi/resources/views/admin/space/email_server/show.blade.php b/flexiapi/resources/views/admin/space/email_server/show.blade.php new file mode 100644 index 0000000..e398f27 --- /dev/null +++ b/flexiapi/resources/views/admin/space/email_server/show.blade.php @@ -0,0 +1,79 @@ +@extends('layouts.main') + +@section('breadcrumb') + + + + +@endsection + +@section('content') +
+

envelope {{ $space->name }}

+
+ + +
+ @csrf + @method('post') +
+ + + @include('parts.errors', ['name' => 'host']) +
+
+ id)value="{{ $emailServer->port }}"@else value="25"@endif"> + + @include('parts.errors', ['name' => 'port']) +
+
+ + + @include('parts.errors', ['name' => 'username']) +
+
+ + + @include('parts.errors', ['name' => 'password']) +
+ +
+ + + @include('parts.errors', ['name' => 'from_address']) +
+
+ + + @include('parts.errors', ['name' => 'from_name']) +
+ +
+ + + @include('parts.errors', ['name' => 'signature']) +
+ +
+ +
+ + +@endsection diff --git a/flexiapi/resources/views/admin/space/integration.blade.php b/flexiapi/resources/views/admin/space/integration.blade.php new file mode 100644 index 0000000..0774973 --- /dev/null +++ b/flexiapi/resources/views/admin/space/integration.blade.php @@ -0,0 +1,39 @@ +@extends('layouts.main') + +@section('breadcrumb') + + + +@endsection + +@section('content') +
+

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

+
+ + @include('admin.space.tabs') + +
+
+ envelope +

{{ __('Email Server') }}

+

+ @if ($space->emailServer) + {{ $space->emailServer->host}}

+ @endif + @if ($space->emailServer) + {{ __('Edit') }} + {{ __('Delete') }} + @else + {{ __('Configure') }} + @endif +

+
+
+@endsection diff --git a/flexiapi/resources/views/admin/space/tabs.blade.php b/flexiapi/resources/views/admin/space/tabs.blade.php index 12907ec..9b2607f 100644 --- a/flexiapi/resources/views/admin/space/tabs.blade.php +++ b/flexiapi/resources/views/admin/space/tabs.blade.php @@ -5,8 +5,10 @@ $items[route('admin.spaces.show', $space->id)] = __('Information'); $items[route('admin.spaces.administration', $space->id)] = __('Administration'); $items[route('admin.spaces.edit', $space->id)] = __('App Configuration'); + $items[route('admin.spaces.integration', $space->id)] = __('Integration'); } else if (auth()->user()->admin) { $items[route('admin.spaces.me')] = __('Information'); + $items[route('admin.spaces.integration', $space->id)] = __('Integration'); } $items[route('admin.spaces.configuration', $space->id)] = __('Configuration'); diff --git a/flexiapi/resources/views/api/documentation.blade.php b/flexiapi/resources/views/api/documentation.blade.php index bc4af9d..3af414f 100644 --- a/flexiapi/resources/views/api/documentation.blade.php +++ b/flexiapi/resources/views/api/documentation.blade.php @@ -1,8 +1,8 @@ @extends('layouts.main', ['welcome' => true]) @section('content') -
- {{-- This view is only a wrapper for the markdown page --}} - {!! $documentation !!} -
+
+ {{-- This view is only a wrapper for the markdown page --}} + {!! $documentation !!} +
@endsection diff --git a/flexiapi/resources/views/api/documentation_markdown.blade.php b/flexiapi/resources/views/api/documentation_markdown.blade.php index c6fd447..b9ad4f4 100644 --- a/flexiapi/resources/views/api/documentation_markdown.blade.php +++ b/flexiapi/resources/views/api/documentation_markdown.blade.php @@ -12,8 +12,7 @@ A `content-type` and `accept` HTTP headers are REQUIRED to use the API properly > accept: application/json ``` -
-
+
Restricted endpoints are protected using a DIGEST authentication or an API Key mechanisms. @@ -111,7 +110,6 @@ A `from` (consisting of the user SIP address, prefixed with `sip:`) header is re You can find more documentation on the related [IETF RFC-7616](https://tools.ietf.org/html/rfc7616). -
# Endpoints @@ -226,6 +224,31 @@ JSON parameters: Delete a domain, **be careful, all the related accounts will also be destroyed**. +### `GET /spaces/{domain}/email` +Super Admin + +Get a space email server configuration + +### `POST /spaces/{domain}/email` +Super Admin + +Update an existing a space email server configuration. + +JSON parameters: + +* `host` **required**, the email server hostname +* `port` **required**, integer, the port +* `username`, the username +* `password`, the password +* `from_address`, email address, the sender email address +* `from_name`, the sender name +* `signature`, a text that will end every emails sent + +### `DELETE /spaces/{domain}/email` +Super Admin + +Delete the a space email server configuration. + ## Account Creation Tokens An `account_creation_token` is a unique token that allow the creation or the validation of a unique account. @@ -860,7 +883,7 @@ JSON parameters: ## Push Notifications ### `POST /push_notification` -User +User Send a push notification using the Flexisip Pusher. diff --git a/flexiapi/resources/views/provisioning/documentation.blade.php b/flexiapi/resources/views/provisioning/documentation.blade.php index bc4af9d..3af414f 100644 --- a/flexiapi/resources/views/provisioning/documentation.blade.php +++ b/flexiapi/resources/views/provisioning/documentation.blade.php @@ -1,8 +1,8 @@ @extends('layouts.main', ['welcome' => true]) @section('content') -
- {{-- This view is only a wrapper for the markdown page --}} - {!! $documentation !!} -
+
+ {{-- This view is only a wrapper for the markdown page --}} + {!! $documentation !!} +
@endsection diff --git a/flexiapi/routes/api.php b/flexiapi/routes/api.php index 240af03..5f7b8a6 100644 --- a/flexiapi/routes/api.php +++ b/flexiapi/routes/api.php @@ -26,6 +26,7 @@ use App\Http\Controllers\Api\Admin\AccountTypeController; use App\Http\Controllers\Api\Admin\ContactsListController; use App\Http\Controllers\Api\Admin\ExternalAccountController; use App\Http\Controllers\Api\Admin\SpaceController; +use App\Http\Controllers\Api\Admin\EmailServerController; use App\Http\Controllers\Api\Admin\VcardsStorageController as AdminVcardsStorageController; use App\Http\Controllers\Api\StatisticsMessageController; use App\Http\Controllers\Api\StatisticsCallController; @@ -62,7 +63,7 @@ Route::get('accounts/me/api_key/{auth_token}', 'Api\Account\ApiKeyController@gen Route::get('phone_countries', 'Api\PhoneCountryController@index'); -Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blocked', 'space.expired']], function () { +Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blocked', 'space.check']], function () { Route::get('accounts/auth_token/{auth_token}/attach', 'Api\Account\AuthTokenController@attach'); Route::post('account_creation_tokens/consume', 'Api\Account\CreationTokenController@consume'); @@ -108,6 +109,12 @@ Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blo Route::put('{domain}', 'update'); Route::delete('{domain}', 'destroy'); }); + + Route::prefix('spaces/{domain}/email')->controller(EmailServerController::class)->group(function () { + Route::get('/', 'show'); + Route::post('/', 'store'); + Route::delete('/', 'destroy'); + }); }); // Account creation token diff --git a/flexiapi/routes/web.php b/flexiapi/routes/web.php index bb9dd2b..83a835c 100644 --- a/flexiapi/routes/web.php +++ b/flexiapi/routes/web.php @@ -41,15 +41,16 @@ use App\Http\Controllers\Admin\ContactsListContactController; use App\Http\Controllers\Admin\ExternalAccountController; use App\Http\Controllers\Admin\PhoneCountryController; use App\Http\Controllers\Admin\ResetPasswordEmailController; -use App\Http\Controllers\Admin\SpaceController; use App\Http\Controllers\Admin\StatisticsController; +use App\Http\Controllers\Admin\SpaceController; +use App\Http\Controllers\Admin\Space\EmailServerController; use Illuminate\Support\Facades\Route; Route::redirect('/', 'login')->name('account.home'); Route::get('documentation', 'Account\AccountController@documentation')->name('account.documentation'); Route::get('about', 'AboutController@about')->name('about'); -Route::middleware(['web_panel_enabled', 'space.expired'])->group(function () { +Route::middleware(['web_panel_enabled', 'space.check'])->group(function () { Route::get('login', 'Account\AuthenticateController@login')->name('account.login'); Route::post('authenticate', 'Account\AuthenticateController@authenticate')->name('account.authenticate'); Route::get('authenticate/qrcode/{token?}', 'Account\AuthenticateController@loginAuthToken')->name('account.authenticate.auth_token'); @@ -87,7 +88,7 @@ Route::name('provisioning.')->prefix('provisioning')->controller(ProvisioningCon Route::get('/', 'show')->name('show'); }); -Route::middleware(['web_panel_enabled', 'space.expired'])->group(function () { +Route::middleware(['web_panel_enabled', 'space.check'])->group(function () { Route::middleware(['public_registration'])->group(function () { Route::redirect('register', 'register/email')->name('account.register'); @@ -156,9 +157,19 @@ Route::middleware(['web_panel_enabled', 'space.expired'])->group(function () { Route::get('auth_tokens/auth/{token}', 'Account\AuthTokenController@auth')->name('auth_tokens.auth'); Route::name('admin.')->prefix('admin')->middleware(['auth.admin', 'auth.check_blocked'])->group(function () { - Route::get('space', 'Admin\SpaceController@me')->name('spaces.me'); - Route::get('spaces/{space}/configuration', 'Admin\SpaceController@configuration')->name('spaces.configuration'); - Route::put('spaces/{space}/configuration', 'Admin\SpaceController@configurationUpdate')->name('spaces.configuration.update'); + Route::name('spaces.')->prefix('spaces')->group(function () { + Route::get('me', 'Admin\SpaceController@me')->name('me'); + Route::get('{space}/configuration', 'Admin\SpaceController@configuration')->name('configuration'); + Route::put('{space}/configuration', 'Admin\SpaceController@configurationUpdate')->name('configuration.update'); + Route::get('{space}/integration', 'Admin\SpaceController@integration')->name('integration'); + + Route::name('email.')->prefix('{space}/email')->controller(EmailServerController::class)->group(function () { + Route::get('/', 'show')->name('show'); + Route::post('/', 'store')->name('store'); + Route::get('delete', 'delete')->name('delete'); + Route::delete('/', 'destroy')->name('destroy'); + }); + }); Route::middleware(['auth.super_admin'])->group(function () { Route::resource('spaces', SpaceController::class); diff --git a/flexiapi/tests/Feature/ApiSpaceEmailServerTest.php b/flexiapi/tests/Feature/ApiSpaceEmailServerTest.php new file mode 100644 index 0000000..ed6cc57 --- /dev/null +++ b/flexiapi/tests/Feature/ApiSpaceEmailServerTest.php @@ -0,0 +1,77 @@ +. +*/ + +namespace Tests\Feature; + +use App\Account; +use App\Space; +use Carbon\Carbon; +use Tests\TestCase; + +class ApiSpaceEmailServerTest extends TestCase +{ + protected $method = 'POST'; + protected $route = '/api/spaces'; + + public function testEmailServer() + { + $admin = Account::factory()->superAdmin()->create(); + $admin->generateApiKey(); + $emailHost = 'email.domain'; + + $route = $this->route . '/' . $admin->space->host . '/email'; + + $this->keyAuthenticated($admin) + ->json($this->method, $route, [ + 'host' => $emailHost, + 'port' => 22 + ]) + ->assertStatus(201); + + $this->keyAuthenticated($admin) + ->json('GET', $route) + ->assertJsonFragment([ + 'host' => $emailHost, + 'port' => 22 + ]) + ->assertStatus(200); + + $this->keyAuthenticated($admin) + ->json($this->method, $route, [ + 'host' => $emailHost, + 'port' => 23 + ]) + ->assertStatus(200); + + $this->keyAuthenticated($admin) + ->json('GET', $route) + ->assertJsonFragment([ + 'port' => 23 + ]) + ->assertStatus(200); + + $this->keyAuthenticated($admin) + ->json('DELETE', $route) + ->assertStatus(200); + + $this->keyAuthenticated($admin) + ->json('GET', $route) + ->assertStatus(404); + } +} diff --git a/flexiapi/tests/TestCase.php b/flexiapi/tests/TestCase.php index 2facd7d..91223ae 100644 --- a/flexiapi/tests/TestCase.php +++ b/flexiapi/tests/TestCase.php @@ -20,7 +20,7 @@ namespace Tests; use App\PhoneCountry; -use App\Http\Middleware\IsSpaceExpired; +use App\Http\Middleware\SpaceCheck; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -37,7 +37,7 @@ abstract class TestCase extends BaseTestCase { parent::setUp(); - $this->withoutMiddleware([IsSpaceExpired::class]); + $this->withoutMiddleware([SpaceCheck::class]); config()->set('app.sip_domain', 'sip.example.com');