Skip to content

Commit

Permalink
feat(statistics): data collect
Browse files Browse the repository at this point in the history
Signed-off-by: Luka Trovic <[email protected]>
  • Loading branch information
luka-nextcloud committed Dec 23, 2024
1 parent f798049 commit 503933a
Show file tree
Hide file tree
Showing 25 changed files with 526 additions and 935 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
/css/
/vendor/
/node_modules/
/backup/

.php-cs-fixer.cache
.phpunit.result.cache
Expand Down
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ docker build -t nextcloud-whiteboard-server -f Dockerfile .

### Reverse proxy

#### Apache < 2.4.47
#### Apache

```
ProxyPass /whiteboard http://localhost:3002/
Expand All @@ -94,12 +94,6 @@ RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?whiteboard/(.*) "ws://localhost:3002/$1" [P,L]
```

#### Apache >= 2.4.47

```
ProxyPass /whiteboard http://localhost:3002/ upgrade=websocket
```

#### Nginx

```
Expand Down
1 change: 1 addition & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The official whiteboard app for Nextcloud. It allows users to create and share w

<background-jobs>
<job>OCA\Whiteboard\BackgroundJob\WatchActiveUsers</job>
<job>OCA\Whiteboard\BackgroundJob\PruneOldStatisticsData</job>
</background-jobs>

<settings>
Expand Down
65 changes: 0 additions & 65 deletions lib/BackgrounJob/WatchActiveUsers.php

This file was deleted.

38 changes: 38 additions & 0 deletions lib/BackgroundJob/PruneOldStatisticsData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Whiteboard\BackgroundJob;

use OCA\Whiteboard\Service\ConfigService;
use OCA\Whiteboard\Service\StatsService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;

class PruneOldStatisticsData extends TimedJob {
public function __construct(
ITimeFactory $time,
protected bool $isCLI,
protected StatsService $statsService,
protected ConfigService $configService,
) {
parent::__construct($time);
$this->setInterval(24 * 60 * 60);
}

protected function run($argument) {
$lifeTimeInDays = $this->configService->getStatisticsDataLifetime();

if (!$lifeTimeInDays) {
return;
}

$beforeTime = time() - $lifeTimeInDays * 24 * 60 * 60;
$this->statsService->pruneData($beforeTime);
}
}
65 changes: 65 additions & 0 deletions lib/BackgroundJob/WatchActiveUsers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Whiteboard\BackgroundJob;

use OCA\Whiteboard\Service\ConfigService;
use OCA\Whiteboard\Service\StatsService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;

class WatchActiveUsers extends TimedJob {
public function __construct(
ITimeFactory $time,
protected bool $isCLI,
protected StatsService $statsService,
protected ConfigService $configService,
) {
parent::__construct($time);
$this->setInterval(300);
}

protected function run($argument) {
$metricsData = $this->getMetricsData();
$activeUsers = $metricsData['totalUsers'] ?? 0;
$this->statsService->insertActiveUsersCount($activeUsers);
}

private function getMetricsData(): array {
$serverUrl = $this->configService->getCollabBackendUrl();
$metricToken = $this->configService->getCollabBackendMetricsToken();

if (!$serverUrl || !$metricToken) {
return [];
}

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $serverUrl . '/metrics');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $metricToken,
]);
$response = curl_exec($curl);
curl_close($curl);

$metrics = [
'totalUsers' => 0,
];

foreach (explode("\n", $response) as $line) {

Check failure on line 55 in lib/BackgroundJob/WatchActiveUsers.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

PossiblyInvalidArgument

lib/BackgroundJob/WatchActiveUsers.php:55:26: PossiblyInvalidArgument: Argument 2 of explode expects string, but possibly different type bool|string provided (see https://psalm.dev/092)
if (strpos($line, 'whiteboard_room_stats{stat="totalUsers"}') === false) {
continue;
}
$parts = explode(' ', $line);
$metrics['totalUsers'] = (int) $parts[1];
}

return $metrics;
}
}
5 changes: 5 additions & 0 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function update(): DataResponse {
$secret = $this->request->getParam('secret');
$enableStatistics = $this->request->getParam('enableStatistics');
$metricsToken = $this->request->getParam('metricsToken');
$statisticsDataLifetime = $this->request->getParam('statisticsDataLifetime');

if ($serverUrl !== null) {
$this->configService->setCollabBackendUrl($serverUrl);
Expand All @@ -54,6 +55,10 @@ public function update(): DataResponse {
$this->configService->setCollabBackendMetricsToken($metricsToken);
}

if ($statisticsDataLifetime !== null) {
$this->configService->setStatisticsDataLifetime((int) $statisticsDataLifetime);
}

return new DataResponse([
'jwt' => $this->jwtService->generateJWTFromPayload([ 'serverUrl' => $serverUrl ])
]);
Expand Down
36 changes: 18 additions & 18 deletions lib/Events/AbstractWhiteboardEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@
use OCP\Files\File;

abstract class AbstractWhiteboardEvent extends Event {
public function __construct(
private File $file,
private User $user,
private array $data,
) {
}

public function getFile(): File {
return $this->file;
}

public function getUser(): User {
return $this->user;
}

public function getData(): array {
return $this->data;
}
public function __construct(
private File $file,
private User $user,
private array $data,
) {
}

public function getFile(): File {
return $this->file;
}

public function getUser(): User {
return $this->user;
}

public function getData(): array {
return $this->data;
}
}
2 changes: 1 addition & 1 deletion lib/Listener/FileCreatedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function handle(Event $event): void {
'type' => 'created',
'share_token' => '',
'fileid' => $node->getId(),
'elements' => '',
'elements' => json_encode([]),
'size' => 0,
'timestamp' => time(),
]);
Expand Down
2 changes: 1 addition & 1 deletion lib/Listener/WhiteboardOpenedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function handle(Event $event): void {
'type' => 'opened',
'share_token' => $user instanceof PublicSharingUser ? $user->getPublicSharingToken() : '',
'fileid' => $file->getId(),
'elements' => $data['elements'] ?? '',
'elements' => json_encode($data['elements'] ?? []),
'size' => $file->getSize(),
'timestamp' => time(),
]);
Expand Down
4 changes: 2 additions & 2 deletions lib/Listener/WhiteboardUpdatedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ public function handle(Event $event): void {

$this->statsService->insertEvent([
'user' => $user->getUID(),
'type' => 'opened',
'type' => 'updated',
'share_token' => $user instanceof PublicSharingUser ? $user->getPublicSharingToken() : '',
'fileid' => $file->getId(),
'elements' => $data['elements'] ?? '',
'elements' => json_encode($data['elements'] ?? []),
'size' => $file->getSize(),
'timestamp' => time(),
]);
Expand Down
5 changes: 3 additions & 2 deletions lib/Migration/Version1000Date20241213132620.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
'default' => 0,
]);
$table->setPrimaryKey(['id']);
$table->addIndex(['user'], 'user_index');
$table->addIndex(['fileid'], 'fileid_index');
$table->addIndex(['user'], 'whiteboard_user_index');
$table->addIndex(['fileid'], 'whiteboard_fileid_index');
}

if (!$schema->hasTable('whiteboard_active_users')) {
Expand All @@ -93,6 +93,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt
'length' => 11,
'default' => 0,
]);
$table->setPrimaryKey(['id'], 'whiteboard_active_users_pk');
}

return $schema;
Expand Down
8 changes: 8 additions & 0 deletions lib/Service/ConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,12 @@ public function setCollabBackendMetricsToken(string $collabBackendMetricsToken):

$this->appConfig->setAppValueString('collabBackendMetricsToken', $collabBackendMetricsToken);
}

public function getStatisticsDataLifetime(): int {
return $this->appConfig->getAppValueInt('statistics_data_lifetime');
}

public function setStatisticsDataLifetime(int $statisticsDataExpiration): void {
$this->appConfig->setAppValueInt('statistics_data_lifetime', $statisticsDataExpiration);
}
}
14 changes: 13 additions & 1 deletion lib/Service/StatsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function insertEvent(array $data): void {
'type' => $queryBuilder->createNamedParameter($data['type']),
'share_token' => $queryBuilder->createNamedParameter($data['share_token']),
'fileid' => $queryBuilder->createNamedParameter($data['fileid']),
'elements' => $queryBuilder->createNamedParameter($data['elements']),
'elements' => $queryBuilder->createNamedParameter($data['elements'] ?? null),
'size' => $queryBuilder->createNamedParameter($data['size']),
'timestamp' => $queryBuilder->createNamedParameter($data['timestamp']),
])
Expand All @@ -41,4 +41,16 @@ public function insertActiveUsersCount(int $count): void {
])
->executeStatement();
}

public function pruneData(int $beforeTime): void {
$queryBuilder = $this->connection->getQueryBuilder();
$queryBuilder->delete('whiteboard_events')
->where($queryBuilder->expr()->lt('timestamp', $queryBuilder->createNamedParameter($beforeTime)))
->executeStatement();

$queryBuilder = $this->connection->getQueryBuilder();
$queryBuilder->delete('whiteboard_active_users')
->where($queryBuilder->expr()->lt('timestamp', $queryBuilder->createNamedParameter($beforeTime)))
->executeStatement();
}
}
1 change: 1 addition & 0 deletions lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function getForm(): TemplateResponse {
$this->initialState->provideInitialState('jwt', $this->jwtService->generateJWTFromPayload([]));
$this->initialState->provideInitialState('enable_statistics', $this->configService->getWhiteboardEnableStatistics());
$this->initialState->provideInitialState('metrics_token', $this->configService->getCollabBackendMetricsToken());
$this->initialState->provideInitialState('statistics_data_lifetime', $this->configService->getStatisticsDataLifetime());
$response = new TemplateResponse(
'whiteboard',
'admin',
Expand Down
Loading

0 comments on commit 503933a

Please sign in to comment.