Page MenuHomePhorge

D1447.1775312435.diff
No OneTemporary

Authored By
Unknown
Size
18 KB
Referenced Files
None
Subscribers
None

D1447.1775312435.diff

diff --git a/src/app/Http/Controllers/API/V4/OpenViduController.php b/src/app/Http/Controllers/API/V4/OpenViduController.php
--- a/src/app/Http/Controllers/API/V4/OpenViduController.php
+++ b/src/app/Http/Controllers/API/V4/OpenViduController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\API\V4;
use App\Http\Controllers\Controller;
+use App\OpenVidu\Room;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
@@ -11,27 +12,40 @@
{
/**
* Join or create the room. Each room has one owner, and the room isn't open until the owner
- * joins (and effectively creates the session.
+ * joins (and effectively creates the session).
*/
public function joinOrCreate($id)
{
$user = Auth::guard()->user();
- $room = \App\OpenVidu\Room::where('name', $id)->first();
+ $room = Room::where('name', $id)->first();
- // this isn't a room, bye bye
+ // This isn't a room, bye bye
if (!$room) {
- return $this->errorResponse(404);
+ return $this->errorResponse(404, \trans('meet.roomnotfound'));
}
- // there's no existing session
+ // There's no existing session
if (!$room->hasSession()) {
- // TODO: only the room owner should be able to create the session
- $room->createSession();
+ // Only the room owner can create the session
+ if ($user->id != $room->user_id) {
+ return $this->errorResponse(423, \trans('meet.sessionnotfound'));
+ }
+
+ $session = $room->createSession();
+
+ if (empty($session)) {
+ return $this->errorResponse(500, \trans('meet.sessioncreateerror'));
+ }
}
+ // Create session token for the current user/connection
$response = $room->getSessionToken('PUBLISHER');
+ if (empty($response)) {
+ return $this->errorResponse(500, \trans('meet.sessionjoinerror'));
+ }
+
if (!empty(request()->input('screenShare'))) {
$add_token = $room->getSessionToken('PUBLISHER');
diff --git a/src/app/OpenVidu/Room.php b/src/app/OpenVidu/Room.php
--- a/src/app/OpenVidu/Room.php
+++ b/src/app/OpenVidu/Room.php
@@ -16,8 +16,15 @@
protected $table = 'openvidu_rooms';
+ /** @var \GuzzleHttp\Client|null HTTP client instance */
private static $client = null;
+
+ /**
+ * Creates HTTP client for connections to OpenVidu server
+ *
+ * @return \GuzzleHttp\Client HTTP client instance
+ */
private function client()
{
if (!self::$client) {
@@ -37,7 +44,12 @@
return self::$client;
}
- public function createSession()
+ /**
+ * Create a OpenVidu session
+ *
+ * @return array|null Session data on success, NULL otherwise
+ */
+ public function createSession(): ?array
{
$response = $this->client()->request(
'POST',
@@ -63,7 +75,12 @@
return $session;
}
- public function getSessionToken($role = 'PUBLISHER')
+ /**
+ * Create a OpenVidu session (connection) token
+ *
+ * @return array|null Token data on success, NULL otherwise
+ */
+ public function getSessionToken($role = 'PUBLISHER'): ?array
{
$response = $this->client()->request(
'POST',
@@ -76,12 +93,21 @@
]
);
- $json = json_decode($response->getBody(), true);
+ if ($response->getStatusCode() == 200) {
+ $json = json_decode($response->getBody(), true);
+
+ return $json;
+ }
- return $json;
+ return null;
}
- public function hasSession()
+ /**
+ * Check if the room has an active session
+ *
+ * @return bool True when the session exists, False otherwise
+ */
+ public function hasSession(): bool
{
if (!$this->session_id) {
return false;
@@ -95,7 +121,7 @@
/**
* The room owner.
*
- * @return \Illuminate\Database\Eloquent\Relations\belongsTo
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
{
diff --git a/src/database/seeds/local/OpenViduRoomSeeder.php b/src/database/seeds/local/OpenViduRoomSeeder.php
--- a/src/database/seeds/local/OpenViduRoomSeeder.php
+++ b/src/database/seeds/local/OpenViduRoomSeeder.php
@@ -5,7 +5,6 @@
use App\OpenVidu\Room;
use Illuminate\Database\Seeder;
-// phpcs:ignore
class OpenViduRoomSeeder extends Seeder
{
/**
diff --git a/src/resources/js/app.js b/src/resources/js/app.js
--- a/src/resources/js/app.js
+++ b/src/resources/js/app.js
@@ -68,7 +68,7 @@
localStorage.setItem('token', '')
delete axios.defaults.headers.common.Authorization
- if (redirect !== false ) {
+ if (redirect !== false) {
this.$router.push({ name: 'login' })
}
},
@@ -120,6 +120,10 @@
if (!error.response) {
// TODO: probably network connection error
} else if (error.response.status === 401) {
+ if (!store.state.afterLogin && this.$router.currentRoute.name != 'login') {
+ store.state.afterLogin = this.$router.currentRoute
+ }
+
this.logoutUser()
} else {
this.errorPage(error.response.status, error.response.statusText)
@@ -131,7 +135,7 @@
// TODO: This method does not show the download progress in the browser
// but it could be implemented in the UI, axios has 'progress' property
axios.get(url, { responseType: 'blob' })
- .then (response => {
+ .then(response => {
const link = document.createElement('a')
const contentDisposition = response.headers['content-disposition']
let filename = 'unknown'
diff --git a/src/resources/lang/en/meet.php b/src/resources/lang/en/meet.php
new file mode 100644
--- /dev/null
+++ b/src/resources/lang/en/meet.php
@@ -0,0 +1,21 @@
+<?php
+
+return [
+
+ /*
+ |--------------------------------------------------------------------------
+ | Pagination Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used by the paginator library to build
+ | the simple pagination links. You are free to change them to anything
+ | you want to customize your views to better match your application.
+ |
+ */
+
+ 'roomnotfound' => 'The room does not exist.',
+ 'sessionnotfound' => 'The session does not exist.',
+ 'sessioncreateerror' => 'Failed to create the session.',
+ 'sessionjoinerror' => 'Failed to join the session.',
+
+];
diff --git a/src/resources/sass/app.scss b/src/resources/sass/app.scss
--- a/src/resources/sass/app.scss
+++ b/src/resources/sass/app.scss
@@ -272,6 +272,29 @@
}
}
+.status-message {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .app-loader {
+ width: auto;
+ position: initial;
+
+ .spinner-border {
+ color: $body-color;
+ }
+ }
+
+ svg {
+ font-size: 1.5em;
+ }
+
+ :first-child {
+ margin-right: 0.4em;
+ }
+}
+
// Various improvements for mobile
@include media-breakpoint-down(sm) {
.card {
@@ -335,37 +358,3 @@
}
}
}
-
-// Openvidu webcomponent improvements
-mat-sidenav-container {
- z-index: 10 !important;
-}
-
-#dialogChooseRoom {
- top: 0;
- left: 0;
- z-index: 10;
- background: #fff;
-
- & > .mat-card {
- max-height: unset;
- margin: 2em auto;
- }
-}
-
-// Openvidu webcomponent improvements
-mat-sidenav-container {
- z-index: 10 !important;
-}
-
-#dialogChooseRoom {
- top: 0;
- left: 0;
- z-index: 10;
- background: #fff;
-
- & > .mat-card {
- max-height: unset;
- margin: 2em auto;
- }
-}
diff --git a/src/resources/vue/App.vue b/src/resources/vue/App.vue
--- a/src/resources/vue/App.vue
+++ b/src/resources/vue/App.vue
@@ -28,8 +28,8 @@
// Release lock on the router-view, otherwise links (e.g. Logout) will not work
// FIXME: This causes dashboard to call /api/auth/info again
this.isLoading = false
- this.$root.errorHandler(error)
this.$root.logoutUser(false)
+ this.$root.errorHandler(error)
})
} else {
this.$root.stopLoading()
diff --git a/src/resources/vue/Meet/Room.vue b/src/resources/vue/Meet/Room.vue
--- a/src/resources/vue/Meet/Room.vue
+++ b/src/resources/vue/Meet/Room.vue
@@ -46,7 +46,8 @@
</div>
</div>
<div class="text-center mt-4 col-sm-12">
- <button class="btn btn-primary pl-5 pr-5">JOIN</button>
+ <status-message :status="roomState" :statusLabels="roomStateLabels"></status-message>
+ <button v-if="roomState == 'ready'" class="btn btn-primary pl-5 pr-5">JOIN</button>
</div>
</form>
</div>
@@ -59,8 +60,12 @@
<script>
import Meet from '../../js/meet/app.js'
+ import StatusMessage from '../Widgets/StatusMessage'
export default {
+ components: {
+ StatusMessage
+ },
data() {
return {
setup: {
@@ -72,21 +77,34 @@
meet: null,
microphone: '',
nickname: '',
- room: null
+ room: null,
+ roomState: 'init',
+ roomStateLabels: {
+ init: 'Checking the room...',
+ 404: 'The room does not exist.',
+ 423: 'The room is closed. Refresh the page to try again.',
+ 500: 'Failed to create a session. Server error.'
+ },
+ session: null
}
},
mounted() {
this.room = this.$route.params.room
if (!this.$store.state.isLoggedIn) {
- this.$store.state.afterLogin = { name: 'room', params: { room: this.room } }
+ this.$store.state.afterLogin = this.$router.currentRoute
this.$router.push({ name: 'login' })
return
}
+ // Initialize OpenVidu and do some basic checks
this.meet = new Meet($('#meet-session')[0]);
-
this.canShareScreen = this.meet.isScreenSharingSupported()
+
+ // Check the room and init the session
+ this.initSession()
+
+ // Setup the room UI
this.setupSession()
},
beforeDestroy() {
@@ -95,22 +113,24 @@
}
},
methods: {
- joinSession() {
- $('#meet-setup').addClass('d-none')
- $('#meet-session-toolbar').removeClass('d-none')
-
- let addUrl = ''
- if (this.canShareScreen) {
- addUrl = '?screenShare=1'
- }
+ initSession() {
+ let addUrl = this.canShareScreen ? '?screenShare=1' : ''
axios.get('/api/v4/meet/openvidu/' + this.room + addUrl)
.then(response => {
// Response data contains: session, token and shareToken
- this.meet.joinRoom(response.data)
+ this.roomState = 'ready'
+ this.session = response.data
$('#app').addClass('meet')
})
- .catch(this.$root.errorHandler)
+ .catch(error => {
+ this.roomState = error.response.status
+ })
+ },
+ joinSession() {
+ $('#meet-setup').addClass('d-none')
+ $('#meet-session-toolbar').removeClass('d-none')
+ this.meet.joinRoom(this.session)
},
leaveSession() {
this.meet.leaveRoom()
diff --git a/src/resources/vue/Widgets/StatusMessage.vue b/src/resources/vue/Widgets/StatusMessage.vue
new file mode 100644
--- /dev/null
+++ b/src/resources/vue/Widgets/StatusMessage.vue
@@ -0,0 +1,51 @@
+<template>
+ <div v-if="status != 'ready'" :class="statusClass()">
+ <div v-if="status == 'init'" class="app-loader small">
+ <div class="spinner-border" role="status"></div>
+ </div>
+ <span v-if="status == 'init'">{{ statusLabel() }}</span>
+
+ <svg-icon v-if="Number(status) >= 400 && status in statusLabels" icon="exclamation-circle"></svg-icon>
+ <span v-if="Number(status) >= 400 && status in statusLabels">{{ statusLabel() }}</span>
+ </div>
+</template>
+
+<script>
+ const defaultLabels = {
+ init: 'Loading...',
+ 404: 'Resource not found.'
+ }
+
+ export default {
+ props: {
+ status: { type: String, default: () => 'init' },
+ statusLabels: { type: Object, default: () => defaultLabels }
+ },
+ data() {
+ return {
+ }
+ },
+ methods: {
+ statusClass() {
+ let className = 'status-message'
+
+ if (Number(this.status) >= 400) {
+ className += ' text-danger'
+ }
+
+ return className
+ },
+ statusLabel() {
+ if (this.status in this.statusLabels) {
+ return this.statusLabels[this.status]
+ }
+
+ if (this.status in defaultLabels) {
+ return defaultLabels[this.status]
+ }
+
+ return ''
+ }
+ }
+ }
+</script>
diff --git a/src/routes/api.php b/src/routes/api.php
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -77,16 +77,9 @@
Route::post('payments/mandate', 'API\V4\PaymentsController@mandateCreate');
Route::put('payments/mandate', 'API\V4\PaymentsController@mandateUpdate');
Route::delete('payments/mandate', 'API\V4\PaymentsController@mandateDelete');
- }
-);
-Route::group(
- [
- 'domain' => \config('app.domain'),
- 'middleware' => 'api',
- 'prefix' => 'v4'
- ],
- function () {
+ // For now we require authenticated users
+ // TODO: Allow guests/external users
Route::get('meet/openvidu/{id}', 'API\V4\OpenViduController@joinOrCreate');
}
);
diff --git a/src/tests/Feature/Controller/DomainsTest.php b/src/tests/Feature/Controller/DomainsTest.php
--- a/src/tests/Feature/Controller/DomainsTest.php
+++ b/src/tests/Feature/Controller/DomainsTest.php
@@ -2,7 +2,6 @@
namespace Tests\Feature\Controller;
-use App\Http\Controllers\API\DomainsController;
use App\Domain;
use App\Entitlement;
use App\Sku;
diff --git a/src/tests/Feature/Controller/OpenViduTest.php b/src/tests/Feature/Controller/OpenViduTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Controller/OpenViduTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Tests\Feature\Controller;
+
+use App\Http\Controllers\API\V4\OpenViduController;
+use App\OpenVidu\Room;
+use Tests\TestCase;
+
+class OpenViduTest extends TestCase
+{
+ /**
+ * Test joining the room
+ *
+ * @group openvidu
+ */
+ public function testJoin(): void
+ {
+ $john = $this->getTestUser('john@kolab.org');
+ $jack = $this->getTestUser('jack@kolab.org');
+ $room = Room::where('name', 'john')->first();
+ $room->session_id = null;
+ $room->save();
+
+ // Unauth access not allowed (yet)
+ $response = $this->get("api/v4/meet/openvidu/{$room->name}");
+ $response->assertStatus(401);
+
+ // Non-existing room name
+ $response = $this->actingAs($john)->get("api/v4/meet/openvidu/non-existing");
+ $response->assertStatus(404);
+
+ // Non-owner, no session yet
+ $response = $this->actingAs($jack)->get("api/v4/meet/openvidu/{$room->name}");
+ $response->assertStatus(423);
+
+ // Room owner
+ $response = $this->actingAs($john)->get("api/v4/meet/openvidu/{$room->name}");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $session_id = $room->fresh()->session_id;
+
+ $this->assertSame('PUBLISHER', $json['role']);
+ $this->assertSame($session_id, $json['session']);
+ $this->assertTrue(is_string($session_id) && !empty($session_id));
+ $this->assertTrue(strpos($json['token'], 'wss://') === 0);
+ $this->assertTrue(!array_key_exists('shareToken', $json));
+
+ $john_token = $json['token'];
+
+ // Non-owner, now the session exists
+ $response = $this->actingAs($jack)->get("api/v4/meet/openvidu/{$room->name}");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('PUBLISHER', $json['role']);
+ $this->assertSame($session_id, $json['session']);
+ $this->assertTrue(strpos($json['token'], 'wss://') === 0);
+ $this->assertTrue($json['token'] != $john_token);
+ $this->assertTrue(!array_key_exists('shareToken', $json));
+
+ $jack_token = $json['token'];
+
+ // request with screenShare token
+ $response = $this->actingAs($john)->get("api/v4/meet/openvidu/{$room->name}?screenShare=1");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('PUBLISHER', $json['role']);
+ $this->assertSame($session_id, $json['session']);
+ $this->assertTrue(strpos($json['token'], 'wss://') === 0);
+ $this->assertTrue($json['token'] != $john_token);
+ $this->assertTrue(strpos($json['shareToken'], 'wss://') === 0);
+ $this->assertTrue($json['shareToken'] != $john_token && $json['shareToken'] != $json['token']);
+ }
+}
diff --git a/src/tests/Feature/Controller/PackagesTest.php b/src/tests/Feature/Controller/PackagesTest.php
--- a/src/tests/Feature/Controller/PackagesTest.php
+++ b/src/tests/Feature/Controller/PackagesTest.php
@@ -2,7 +2,6 @@
namespace Tests\Feature\Controller;
-use App\Http\Controllers\API\PackagesController;
use App\Package;
use Tests\TestCase;

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 4, 2:20 PM (17 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18829915
Default Alt Text
D1447.1775312435.diff (18 KB)

Event Timeline