Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117848222
D1447.1775312435.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
18 KB
Referenced Files
None
Subscribers
None
D1447.1775312435.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D1447: OpenVidu integration
Attached
Detach File
Event Timeline