Skip to content

Commit

Permalink
Fix server crashed, regular cleanups
Browse files Browse the repository at this point in the history
Signed-off-by: Hoang Pham <[email protected]>
  • Loading branch information
hweihwang committed Dec 30, 2024
1 parent 4aadede commit b97985f
Show file tree
Hide file tree
Showing 22 changed files with 827 additions and 289 deletions.
42 changes: 42 additions & 0 deletions tests/integration/configMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const defaultMockValues = {
IS_TEST_ENV: true,
BYPASS_SSL_VALIDATION: false,
USE_TLS: false,
TLS_KEY_PATH: null,
TLS_CERT_PATH: null,
STORAGE_STRATEGY: 'lru',
REDIS_URL: null,
FORCE_CLOSE_TIMEOUT: 60 * 1000,
METRICS_TOKEN: null,
JWT_SECRET_KEY: null,
BACKUP_DIR: './backup',
ROOM_CLEANUP_INTERVAL: 1000,
LOCK_TIMEOUT: 1000,
LOCK_RETRY_INTERVAL: 1000,
MAX_BACKUPS_PER_ROOM: 10,
ROOM_MAX_AGE: 1000,
MAX_ROOMS_IN_STORAGE: 1000,
}

export function createConfigMock(customValues = {}) {
const mockValues = { ...defaultMockValues, ...customValues }

const computedProperties = {
get JWT_SECRET_KEY() {
return mockValues.JWT_SECRET_KEY
},
get NEXTCLOUD_WEBSOCKET_URL() {
return mockValues.NEXTCLOUD_WEBSOCKET_URL
},
get NEXTCLOUD_URL() {
return mockValues.NEXTCLOUD_URL
},
}

const mockConfig = {
...mockValues,
...computedProperties,
}

return mockConfig
}
35 changes: 20 additions & 15 deletions tests/integration/metrics.spec.mjs
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
import { beforeAll, afterAll, describe, it, expect, vi } from 'vitest'
import axios from 'axios'
import ServerManager from '../../websocket_server/ServerManager.js'
import { createConfigMock } from './configMock.js'
import ServerManagerModule from '../../websocket_server/ServerManager.js'
import ConfigModule from '../../websocket_server/Config.js'

const SERVER_URL = 'http://localhost:3008'
const SECRET = 'secret'
vi.mock('../../websocket_server/Config.js', () => ({
default: createConfigMock({
NEXTCLOUD_URL: 'http://localhost:3008',
NEXTCLOUD_WEBSOCKET_URL: 'http://localhost:3008',
PORT: '3008',
METRICS_TOKEN: 'secret',
}),
}))

vi.stubEnv('METRICS_TOKEN', SECRET)
const Config = ConfigModule
const ServerManager = ServerManagerModule

describe('Metrics endpoint', () => {
let serverManager

beforeAll(async () => {
serverManager = new ServerManager({
port: 3008,
storageStrategy: 'lru',
})

serverManager.start()
serverManager = new ServerManager()
await serverManager.start()
})

afterAll(async () => {
await serverManager.server.close()
await serverManager.gracefulShutdown()
})

it('should work with bearer auth', async () => {
const response = await axios.get(`${SERVER_URL}/metrics`, {
const response = await axios.get(`${Config.NEXTCLOUD_URL}/metrics`, {
headers: {
Authorization: `Bearer ${SECRET}`,
Authorization: `Bearer ${Config.METRICS_TOKEN}`,
},
})
expect(response.status).toBe(200)
Expand All @@ -39,14 +44,14 @@ describe('Metrics endpoint', () => {
})

it('should work with token param', async () => {
const response = await axios.get(`${SERVER_URL}/metrics?token=${SECRET}`)
const response = await axios.get(`${Config.NEXTCLOUD_URL}/metrics?token=${Config.METRICS_TOKEN}`)
expect(response.status).toBe(200)
expect(response.data).toContain('whiteboard_room_stats{stat="activeRooms"}')
})

it('Not return on invalid auth', async () => {
try {
await axios.get(`${SERVER_URL}/metrics`, {
await axios.get(`${Config.NEXTCLOUD_URL}/metrics`, {
headers: {
Authorization: 'Bearer wrongtoken',
},
Expand Down
46 changes: 26 additions & 20 deletions tests/integration/socket.spec.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { beforeAll, afterAll, describe, it, expect, vi } from 'vitest'
import ServerManager from '../../websocket_server/ServerManager.js'
import io from 'socket.io-client'
import { io } from 'socket.io-client'
import jwt from 'jsonwebtoken'
import Utils from '../../websocket_server/Utils.js'
import { createConfigMock } from './configMock.js'
import ServerManagerModule from '../../websocket_server/ServerManager.js'
import UtilsModule from '../../websocket_server/Utils.js'
import ConfigModule from '../../websocket_server/Config.js'

const SERVER_URL = 'http://localhost:3009'
const SECRET = 'secret'
vi.mock('../../websocket_server/Config.js', () => ({
default: createConfigMock({
NEXTCLOUD_URL: 'http://localhost:3009',
NEXTCLOUD_WEBSOCKET_URL: 'http://localhost:3009',
PORT: '3009',
JWT_SECRET_KEY: 'secret',
}),
}))

vi.stubEnv('JWT_SECRET_KEY', SECRET)
const Config = ConfigModule
const ServerManager = ServerManagerModule
const Utils = UtilsModule

function waitFor(socket, event) {
return new Promise((resolve) => {
Expand All @@ -19,16 +29,12 @@ describe('Socket handling', () => {
let serverManager, socket

beforeAll(async () => {
serverManager = new ServerManager({
port: 3009,
storageStrategy: 'lru',
})

serverManager.start()
serverManager = new ServerManager()
await serverManager.start()

socket = io(SERVER_URL, {
socket = io(Config.NEXTCLOUD_WEBSOCKET_URL, {
auth: {
token: jwt.sign({ roomID: 123, user: { name: 'Admin' } }, SECRET),
token: jwt.sign({ roomID: 123, user: { name: 'Admin' } }, Config.JWT_SECRET_KEY),
},
})

Expand All @@ -39,11 +45,11 @@ describe('Socket handling', () => {

afterAll(async () => {
await socket.disconnect()
await serverManager.server.close()
await serverManager.gracefulShutdown()
})

it('socket invalid jwt', async () => {
const socket = io(SERVER_URL, {
const socket = io(Config.NEXTCLOUD_WEBSOCKET_URL, {
auth: {
token: jwt.sign({ roomID: 123, user: { name: 'Admin' } }, 'wrongsecret'),
},
Expand All @@ -56,9 +62,9 @@ describe('Socket handling', () => {
})

it('socket valid jwt', async () => {
const socket = io(SERVER_URL, {
const socket = io(Config.NEXTCLOUD_WEBSOCKET_URL, {
auth: {
token: jwt.sign({ roomID: 123, user: { name: 'Admin' } }, SECRET),
token: jwt.sign({ roomID: 123, user: { name: 'Admin' } }, Config.JWT_SECRET_KEY),
},
})
return new Promise((resolve) => {
Expand All @@ -78,9 +84,9 @@ describe('Socket handling', () => {
})

it('read only socket', async () => {
const socket = io(SERVER_URL, {
const socket = io(Config.NEXTCLOUD_WEBSOCKET_URL, {
auth: {
token: jwt.sign({ roomID: 123, user: { name: 'Admin' }, isFileReadOnly: true }, SECRET),
token: jwt.sign({ roomID: 123, user: { name: 'Admin' }, isFileReadOnly: true }, Config.JWT_SECRET_KEY),
},
})
return new Promise((resolve) => {
Expand Down
14 changes: 7 additions & 7 deletions vitest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
environment: 'node',
include: [
'tests/integration/*.spec.?(c|m)[jt]s?(x)'
],
},
})
test: {
environment: 'node',
include: [
'tests/integration/*.spec.?(c|m)[jt]s?(x)'
],
},
})
12 changes: 4 additions & 8 deletions websocket_server/ApiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@

import fetch from 'node-fetch'
import https from 'https'
import dotenv from 'dotenv'
import Utils from './Utils.js'
dotenv.config()
import Config from './Config.js'

export default class ApiService {

constructor(tokenGenerator) {
this.NEXTCLOUD_URL = process.env.NEXTCLOUD_URL
this.IS_DEV = Utils.parseBooleanFromEnv(process.env.IS_DEV)
this.agent = this.IS_DEV ? new https.Agent({ rejectUnauthorized: false }) : null
this.agent = (Config.USE_TLS && Config.BYPASS_SSL_VALIDATION) ? new https.Agent({ rejectUnauthorized: false }) : null
this.tokenGenerator = tokenGenerator
}

Expand Down Expand Up @@ -50,15 +46,15 @@ export default class ApiService {
}

async getRoomDataFromServer(roomID, jwtToken) {
const url = `${this.NEXTCLOUD_URL}/index.php/apps/whiteboard/${roomID}`
const url = `${Config.NEXTCLOUD_URL}/index.php/apps/whiteboard/${roomID}`
const options = this.fetchOptions('GET', jwtToken)
return this.fetchData(url, options)
}

async saveRoomDataToServer(roomID, roomData, lastEditedUser, files) {
console.log(`[${roomID}] Saving room data to server: ${roomData.length} elements, ${Object.keys(files).length} files`)

const url = `${this.NEXTCLOUD_URL}/index.php/apps/whiteboard/${roomID}`
const url = `${Config.NEXTCLOUD_URL}/index.php/apps/whiteboard/${roomID}`

const body = {
data: {
Expand Down
13 changes: 4 additions & 9 deletions websocket_server/AppManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import dotenv from 'dotenv'
import express from 'express'
import PrometheusDataManager from './PrometheusDataManager.js'

dotenv.config()
import Config from './Config.js'

export default class AppManager {

constructor(storageManager) {
constructor(metricsManager) {
this.app = express()
this.storageManager = storageManager
this.metricsManager = new PrometheusDataManager(storageManager)
this.METRICS_TOKEN = process.env.METRICS_TOKEN
this.metricsManager = metricsManager
this.setupRoutes()
}

Expand All @@ -30,7 +25,7 @@ export default class AppManager {

async metricsHandler(req, res) {
const token = req.headers.authorization?.split(' ')[1] || req.query.token
if (!this.METRICS_TOKEN || token !== this.METRICS_TOKEN) {
if (!Config.METRICS_TOKEN || token !== Config.METRICS_TOKEN) {
return res.status(403).send('Unauthorized')
}
this.metricsManager.updateMetrics()
Expand Down
Loading

0 comments on commit b97985f

Please sign in to comment.