Skip to content

Commit 6606f6d

Browse files
committed
feat: read only handler
Signed-off-by: Hoang Pham <[email protected]>
1 parent 03f28d4 commit 6606f6d

File tree

5 files changed

+78
-284
lines changed

5 files changed

+78
-284
lines changed

lib/Controller/WhiteboardController.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
66
* SPDX-License-Identifier: AGPL-3.0-or-later
77
*/
8+
89
namespace OCA\Whiteboard\Controller;
910

1011
use Firebase\JWT\JWT;
@@ -33,14 +34,42 @@ public function __construct(
3334
parent::__construct($appName, $request);
3435
}
3536

37+
/**
38+
* @throws NotPermittedException
39+
* @throws NoUserException
40+
* @throws \JsonException
41+
*/
3642
#[NoAdminRequired]
3743
#[NoCSRFRequired]
3844
#[PublicPage]
3945
public function update(int $fileId, array $data): DataResponse {
40-
$user = $this->userSession->getUser();
41-
$userFolder = $this->rootFolder->getUserFolder($user?->getUID());
46+
$authHeader = $this->request->getHeader('Authorization');
47+
48+
if (!$authHeader) {
49+
return new DataResponse(['message' => 'Unauthorized'], Http::STATUS_UNAUTHORIZED);
50+
}
51+
52+
[$jwt] = sscanf($authHeader, 'Bearer %s');
53+
54+
if (!$jwt) {
55+
return new DataResponse(['message' => 'Unauthorized'], Http::STATUS_UNAUTHORIZED);
56+
}
57+
58+
try {
59+
$key = $this->config->getSystemValueString('jwt_secret_key');
60+
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
61+
$userId = $decoded->userid;
62+
} catch (\Exception $e) {
63+
return new DataResponse(['message' => 'Unauthorized'], Http::STATUS_UNAUTHORIZED);
64+
}
65+
66+
$userFolder = $this->rootFolder->getUserFolder($userId);
4267
$file = $userFolder->getById($fileId)[0];
4368

69+
if (empty($data)) {
70+
$data = ['elements' => [], 'scrollToContent' => true];
71+
}
72+
4473
$file->putContent(json_encode($data, JSON_THROW_ON_ERROR));
4574

4675
return new DataResponse(['status' => 'success']);
@@ -79,9 +108,11 @@ public function show(int $fileId): DataResponse {
79108
$file = $userFolder->getById($fileId)[0];
80109

81110
$fileContent = $file->getContent();
82-
if ($fileContent === '') {
111+
112+
if (empty($fileContent)) {
83113
$fileContent = '{"elements":[],"scrollToContent":true}';
84114
}
115+
85116
$data = json_decode($fileContent, true, 512, JSON_THROW_ON_ERROR);
86117

87118
return new DataResponse([

websocket_server/index.js

Lines changed: 0 additions & 249 deletions
This file was deleted.

websocket_server/roomData.js

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* eslint-disable no-console */
2-
/* eslint-disable n/no-process-exit */
32

43
import fetch from 'node-fetch'
54
import dotenv from 'dotenv'
@@ -9,16 +8,15 @@ dotenv.config()
98
const {
109
NEXTCLOUD_URL = 'http://nextcloud.local',
1110
ADMIN_USER = 'admin',
12-
ADMIN_PASS = 'admin',
11+
ADMIN_PASS = 'admin'
1312
} = process.env
14-
const FORCE_CLOSE_TIMEOUT = 60 * 1000
1513

16-
export let roomDataStore = {}
14+
export const roomDataStore = {}
1715

1816
const fetchOptions = (method, token, body = null) => {
1917
const headers = {
2018
'Content-Type': 'application/json',
21-
Authorization: `Bearer ${token}`,
19+
Authorization: `Bearer ${token}`
2220
}
2321

2422
if (method === 'PUT') {
@@ -28,7 +26,7 @@ const fetchOptions = (method, token, body = null) => {
2826
return {
2927
method,
3028
headers,
31-
...(body && { body: JSON.stringify(body) }),
29+
...(body && { body: JSON.stringify(body) })
3230
}
3331
}
3432

@@ -58,6 +56,7 @@ export const getRoomDataFromFile = async (roomID, socket) => {
5856
return result ? result.data.elements : null
5957
}
6058

59+
// Called when there's nobody in the room (No one keeping the latest data), BE to BE communication
6160
export const saveRoomDataToFile = async (roomID, data) => {
6261
console.log(`Saving room data to file: ${roomID}`)
6362
const url = `${NEXTCLOUD_URL}/index.php/apps/whiteboard/${roomID}`
@@ -67,27 +66,16 @@ export const saveRoomDataToFile = async (roomID, data) => {
6766
await fetchData(url, options)
6867
}
6968

69+
// TODO: Should be called when the server is shutting down and a should be a BE to BE (or OS) communication
70+
// in batch operation, run in background and check if it's necessary to save for each room.
71+
// Should be called periodically and saved somewhere else for preventing data loss (memory loss, server crash, electricity cut, etc.)
7072
export const saveAllRoomsData = async () => {
73+
}
74+
75+
export const removeAllRoomData = async () => {
7176
for (const roomID in roomDataStore) {
72-
if (Object.prototype.hasOwnProperty.call(roomDataStore, roomID) && roomDataStore[roomID]) {
73-
await saveRoomDataToFile(roomID, roomDataStore[roomID])
77+
if (Object.prototype.hasOwnProperty.call(roomDataStore, roomID)) {
78+
delete roomDataStore[roomID]
7479
}
7580
}
7681
}
77-
78-
export const gracefulShutdown = async (server) => {
79-
console.log('Received shutdown signal, saving all data...')
80-
await saveAllRoomsData()
81-
console.log('All data saved, shutting down server...')
82-
roomDataStore = {}
83-
84-
server.close(() => {
85-
console.log('HTTP server closed.')
86-
process.exit(0)
87-
})
88-
89-
setTimeout(() => {
90-
console.error('Force closing server after 1 minute.')
91-
process.exit(1)
92-
}, FORCE_CLOSE_TIMEOUT)
93-
}

0 commit comments

Comments
 (0)