Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix server crashed, regular cleanups, improve configs, etc... #306

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) ? new https.Agent({ rejectUnauthorized: !Config.BYPASS_SSL_VALIDATION }) : 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
Loading