show bridge process state and log

Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
This commit is contained in:
Julien Veyssier 2020-08-31 12:18:18 +02:00
Родитель f0561d0357
Коммит b46f39e70d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4141FEE162030638
5 изменённых файлов: 136 добавлений и 17 удалений

Просмотреть файл

@ -395,6 +395,15 @@ return [
'token' => '^[a-z0-9]{4,30}$',
],
],
[
'name' => 'Matterbridge#getBridgeProcessState',
'url' => '/api/{apiVersion}/bridge/{token}/process',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v1',
'token' => '^[a-z0-9]{4,30}$',
],
],
[
'name' => 'Matterbridge#editBridgeOfRoom',
'url' => '/api/{apiVersion}/bridge/{token}',

Просмотреть файл

@ -60,11 +60,27 @@ class MatterbridgeController extends AEnvironmentAwareController {
* @return DataResponse
*/
public function getBridgeOfRoom(): DataResponse {
$this->bridgeManager->checkBridge($this->room);
$pid = $this->bridgeManager->checkBridge($this->room);
$logContent = $this->bridgeManager->getBridgeLog($this->room);
$bridge = $this->bridgeManager->getBridgeOfRoom($this->room);
$bridge['running'] = ($pid !== 0);
$bridge['log'] = $logContent;
return new DataResponse($bridge);
}
/**
* Get bridge process information
*
* @NoAdminRequired
* @RequireLoggedInModeratorParticipant
*
* @return DataResponse
*/
public function getBridgeProcessState(): DataResponse {
$result = $this->bridgeManager->getBridgeProcessState($this->room);
return new DataResponse($result);
}
/**
* Edit bridge information of one room
*

Просмотреть файл

@ -92,6 +92,36 @@ class MatterbridgeManager {
return $this->getBridgeFromDb($room);
}
/**
* Get bridge process information for a specific room
*
* @param Room $room the room
* @return array process state and log
*/
public function getBridgeProcessState(Room $room): array {
$bridge = $this->getBridgeFromDb($room);
$logContent = $this->getBridgeLog($room);
$pid = $this->checkBridgeProcess($room, $bridge, false);
return [
'running' => ($pid !== 0),
'log' => $logContent
];
}
/**
* Get bridge log file content
*
* @param Room $room the room
* @return string log file content
*/
public function getBridgeLog(Room $room): string {
$outputPath = sprintf('/tmp/bridge-%s.log', $room->getToken());
$logContent = file_get_contents($outputPath);
return $logContent ?? '';
}
/**
* Edit bridge information for a room
*
@ -153,8 +183,9 @@ class MatterbridgeManager {
/**
* For one room, check mattermost process respects desired state
* @param Room $room the room
* @return int the bridge process ID
*/
public function checkBridge(Room $room): void {
public function checkBridge(Room $room): int {
$bridge = $this->getBridgeOfRoom($room);
$pid = $this->checkBridgeProcess($room, $bridge);
if ($pid !== $bridge['pid']) {
@ -162,6 +193,7 @@ class MatterbridgeManager {
$bridge['pid'] = $pid;
$this->saveBridgeToDb($room, $bridge);
}
return $pid;
}
private function getDataFolder(): ISimpleFolder {
@ -437,43 +469,48 @@ class MatterbridgeManager {
*
* @param Room $room the room
* @param array $bridge bridge information
* @param $relaunch whether to launch the process if it's down but bridge is enabled
* @return int the corresponding matterbridge process ID, 0 if none
*/
private function checkBridgeProcess(Room $room, array $bridge): int {
private function checkBridgeProcess(Room $room, array $bridge, bool $relaunch = true): int {
$pid = 0;
if (isset($bridge['pid']) && intval($bridge['pid']) !== 0) {
// config : there is a PID stored
$pid = intval($bridge['pid']);
$isRunning = $this->isRunning($pid);
$isRunning = $this->isRunning($bridge['pid']);
// if bridge running and enabled is false : kill it
if ($isRunning) {
if ($bridge['enabled']) {
$this->logger->info('Process running AND bridge enabled in config : doing nothing');
$pid = $bridge['pid'];
} else {
$this->logger->info('Process running AND bridge disabled in config : KILL ' . $pid);
$killed = $this->killPid($pid);
$this->logger->info('Process running AND bridge disabled in config : KILL ' . $bridge['pid']);
$killed = $this->killPid($bridge['pid']);
if ($killed) {
$pid = 0;
} else {
$this->logger->info('Impossible to kill ' . $pid);
throw new ImpossibleToKillException('Impossible to kill bridge process [' . $pid . ']');
$this->logger->info('Impossible to kill ' . $bridge['pid']);
throw new ImpossibleToKillException('Impossible to kill bridge process [' . $bridge['pid'] . ']');
}
}
} else {
// no process found
if ($bridge['enabled']) {
$this->logger->info('Process not found AND bridge enabled in config : relaunching');
$pid = $this->launchMatterbridge($room);
if ($relaunch) {
$this->logger->info('Process not found AND bridge enabled in config : relaunching');
$pid = $this->launchMatterbridge($room);
}
} else {
$this->logger->info('Process not found AND bridge disabled in config : doing nothing');
}
}
} elseif ($bridge['enabled']) {
// config : no PID stored
// config : enabled => launch it
$pid = $this->launchMatterbridge($room);
$this->logger->info('Launch process, PID is '.$pid);
if ($relaunch) {
// config : no PID stored
// config : enabled => launch it
$pid = $this->launchMatterbridge($room);
$this->logger->info('Launch process, PID is '.$pid);
}
} else {
$this->logger->info('No PID defined in config AND bridge disabled in config : doing nothing');
}

Просмотреть файл

@ -40,7 +40,17 @@
:checked="enabled"
@update:checked="onEnabled">
{{ t('spreed', 'Enabled') }}
({{ processStateText }})
</ActionCheckbox>
<button class="" @click="showLogContent">
{{ t('spreed', 'Show matterbridge log') }}
</button>
<Modal v-if="logModal"
@close="closeLogModal">
<div class="modal__content">
<textarea v-model="processLog" class="log-content" />
</div>
</Modal>
<Multiselect
ref="partMultiselect"
v-model="selectedType"
@ -73,11 +83,13 @@
import {
editBridge,
getBridge,
getBridgeProcessState,
} from '../../../services/matterbridgeService'
import { showSuccess } from '@nextcloud/dialogs'
import ActionCheckbox from '@nextcloud/vue/dist/Components/ActionCheckbox'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
import Modal from '@nextcloud/vue/dist/Components/Modal'
import BridgePart from './BridgePart'
export default {
@ -87,6 +99,7 @@ export default {
ActionButton,
Multiselect,
BridgePart,
Modal,
},
mixins: [
@ -100,6 +113,9 @@ export default {
enabled: false,
parts: [],
loading: false,
processRunning: null,
processLog: '',
logModal: false,
types: {
nctalk: {
name: t('spreed', 'Nextcloud Talk'),
@ -430,6 +446,13 @@ export default {
return p.type !== 'nctalk' || p.channel !== this.token
})
},
processStateText() {
return this.processRunning === null
? t('spreed', 'unknown state')
: this.processRunning
? t('spreed', 'running')
: t('spreed', 'not running')
},
},
beforeMount() {
@ -460,17 +483,17 @@ export default {
this.onSave()
},
onSave() {
console.debug(this.parts)
this.editBridge(this.token, this.enabled, this.parts)
},
async getBridge(token) {
this.loading = true
try {
const result = await getBridge(token)
console.debug(result)
const bridge = result.data.ocs.data
this.enabled = bridge.enabled
this.parts = bridge.parts
this.processLog = bridge.log
this.processRunning = bridge.running
} catch (exception) {
console.debug(exception)
}
@ -485,6 +508,24 @@ export default {
console.debug(exception)
}
this.loading = false
setTimeout(() => this.getBridgeProcessState(this.token), 4000)
},
async getBridgeProcessState(token) {
try {
const result = await getBridgeProcessState(token)
this.processLog = result.data.ocs.data.log
this.processRunning = result.data.ocs.data.running
console.debug(result.data.ocs.data.log)
} catch (exception) {
console.debug(exception)
}
},
showLogContent() {
this.getBridgeProcessState(this.token)
this.logModal = true
},
closeLogModal() {
this.logModal = false
},
},
}
@ -514,6 +555,7 @@ export default {
}
.basic-settings {
button,
.multiselect {
width: calc(100% - 40px);
margin-left: 40px;
@ -523,4 +565,9 @@ export default {
ul {
margin-bottom: 64px;
}
.log-content {
width: 600px;
height: 400px;
}
</style>

Просмотреть файл

@ -50,6 +50,15 @@ const getBridge = async function(token) {
return response
}
/**
* Get the bridge binary state for a room
* @param {token} token the conversation token.
*/
const getBridgeProcessState = async function(token) {
const response = await axios.get(generateOcsUrl('apps/spreed/api/v1', 2) + `bridge/${token}/process`)
return response
}
/**
* Ask to stop all bridges (and kill all related processes)
*/
@ -71,6 +80,7 @@ const getMatterbridgeVersion = async function() {
export {
editBridge,
getBridge,
getBridgeProcessState,
stopAllBridges,
getMatterbridgeVersion,
enableMatterbridgeApp,