Skip to content

Commit

Permalink
Expose the config on the settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
rtm516 committed Jul 19, 2024
1 parent 36557c3 commit 70dc3f0
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 43 deletions.
6 changes: 3 additions & 3 deletions bootstrap/manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The manager is designed for bigger deployments and not recommended for smaller o

## Installation
1. Download the latest release file `MCXboxBroadcastManager.jar`
2. Set the `SPRING_DATA_MONGODB_URI` environment variable to the MongoDB connection string
2. Set the `SPRING_DATA_MONGODB_URI` environment variable to the MongoDB connection string (or change the settings in the `application.yaml`)
3. Start the jar file using `java -jar MCXboxBroadcastManager.jar`
4. Open the web page, port 8082 by default
5. Login with the default credentials `admin:password`
Expand All @@ -16,6 +16,6 @@ The image can be found at `ghcr.io/rtm516/mcxboxbroadcast-manager:latest`
There is also a prebuilt Docker compose file that can be used to run the manager and the mongodb together. This can be found at [`docker-compose.yml`](docker-compose.yml)

## Additional Configuration
Configuration can be made in the `application.yml` mounted into `/opt/app/config` (supports a range of spring properties)
Configuration can be made in the `application.yaml` mounted into `/opt/app/config` (supports a range of spring properties)

See [application.yml](src/main/resources/application.yml) for the default configuration
See [application.yaml](src/main/resources/application.yaml) for the default configuration
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public BackendManager(UserCollection userCollection, PasswordEncoder passwordEnc
this.scheduledThreadPool = Executors.newScheduledThreadPool(mainConfig.workerThreads(), new NamedThreadFactory("MCXboxBroadcast Manager Thread"));

// Create the admin user if it doesn't exist
if (authEnabled() && userCollection.findUserByUsername("admin").isEmpty()) {
if (mainConfig.auth() && userCollection.findUserByUsername("admin").isEmpty()) {
userCollection.save(new User("admin", passwordEncoder.encode("password")));
}

Expand All @@ -56,20 +56,11 @@ public ScheduledExecutorService scheduledThreadPool() {
}

/**
* Check if authentication is enabled
* Get the main config
*
* @return if authentication is enabled
* @return the main config
*/
public boolean authEnabled() {
return mainConfig.auth();
}

/**
* Get the time in seconds between each update
*
* @return the time in seconds between each update
*/
public MainConfig.UpdateTime updateTime() {
return mainConfig.updateTime();
public MainConfig config() {
return mainConfig;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public ServerManager(BackendManager backendManager, ServerCollection serverColle
for (ServerContainer serverContainer : servers.values()) {
serverContainer.updateSessionInfo();
}
}, 0, backendManager.updateTime().server(), TimeUnit.SECONDS);
}, 0, backendManager.config().updateTime().server(), TimeUnit.SECONDS);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, BackendManager backend
http
.csrf(csrf -> csrf.disable()); // Disable CSRF - TODO Implement support in the react app

if (backendManager.authEnabled()) {
if (backendManager.config().auth()) {
http
.authorizeHttpRequests(authorize ->
authorize
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
package com.rtm516.mcxboxbroadcast.manager.controllers;

import com.rtm516.mcxboxbroadcast.core.BuildData;
import com.rtm516.mcxboxbroadcast.manager.BackendManager;
import com.rtm516.mcxboxbroadcast.manager.config.MainConfig;
import com.rtm516.mcxboxbroadcast.manager.models.response.ManagerInfoResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.management.ManagementFactory;
import java.util.Map;

@RestController
public class MiscController {
private final BackendManager backendManager;

@Autowired
public MiscController(BackendManager backendManager) {
this.backendManager = backendManager;
}

@GetMapping("/health")
public void health(HttpServletResponse response) {
response.setStatus(200);
Expand All @@ -20,4 +31,17 @@ public ManagerInfoResponse info(HttpServletResponse response) {
response.setStatus(200);
return new ManagerInfoResponse(BuildData.VERSION, (int) (ManagementFactory.getRuntimeMXBean().getUptime() / 1000));
}

@GetMapping("/api/settings")
public Map<String, String> settings(HttpServletResponse response) {
response.setStatus(200);

MainConfig config = backendManager.config();

return Map.of(
"Authentication", String.valueOf(config.auth()),
"Update time (s)", "Server: " + config.updateTime().server() + ", Session: " + config.updateTime().session() + ", Stats: " + config.updateTime().stats() + ", Friend: " + config.updateTime().friend(),
"Worker Threads", String.valueOf(config.workerThreads())
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public void start() {

sessionManager.restartCallback(this::restart);
try {
sessionManager.init(botManager.serverSessionInfo(bot.serverId()), new FriendSyncConfig(botManager.backendManager().updateTime().friend(), true, true));
sessionManager.init(botManager.serverSessionInfo(bot.serverId()), new FriendSyncConfig(botManager.backendManager().config().updateTime().friend(), true, true));
status = Status.ONLINE;

bot.gamertag(sessionManager.getGamertag());
Expand All @@ -108,8 +108,8 @@ public void start() {
// Force update the friend list so we have it sooner for the UI
sessionManager.friendManager().get();

sessionManager.scheduledThread().scheduleWithFixedDelay(this::updateSessionInfo, botManager.backendManager().updateTime().session(), botManager.backendManager().updateTime().session(), TimeUnit.SECONDS);
sessionManager.scheduledThread().scheduleWithFixedDelay(this::updateFriendStats, 0, botManager.backendManager().updateTime().stats(), TimeUnit.SECONDS);
sessionManager.scheduledThread().scheduleWithFixedDelay(this::updateSessionInfo, botManager.backendManager().config().updateTime().session(), botManager.backendManager().config().updateTime().session(), TimeUnit.SECONDS);
sessionManager.scheduledThread().scheduleWithFixedDelay(this::updateFriendStats, 0, botManager.backendManager().config().updateTime().stats(), TimeUnit.SECONDS);
} catch (SessionCreationException | SessionUpdateException e) {
logger.error("Failed to create session", e);
status = Status.OFFLINE;
Expand Down
8 changes: 4 additions & 4 deletions bootstrap/manager/src/ui/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:react-hooks/recommended'
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
{ allowConstantExport: true }
]
}
}
4 changes: 2 additions & 2 deletions bootstrap/manager/src/ui/src/pages/Settings.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import GeneralPanel from '../settings/GeneralPanel'
import ConfigPanel from '../settings/ConfigPanel'
import UserPanel from '../settings/UserPanel'
import AboutPanel from '../settings/AboutPanel'

function Settings () {
return (
<>
<div className='px-8 pb-12 flex items-center flex-col gap-5'>
<GeneralPanel />
<ConfigPanel />
<UserPanel />
<AboutPanel />
</div>
Expand Down
11 changes: 10 additions & 1 deletion bootstrap/manager/src/ui/src/settings/AboutPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ function AboutPanel () {
fetch('/api/info').then((res) => res.json()).then((data) => {
setInfo(data)
})

// Update uptime every second
const ticker = setInterval(() => {
setInfo((prevInfo) => {
return { ...prevInfo, uptime: prevInfo.uptime + 1 }
})
}, 1000)

return () => clearInterval(ticker)
}, [])

function formatTime (dateString) {
Expand All @@ -23,7 +32,7 @@ function AboutPanel () {
if (seconds > 0) timeSince.push(`${seconds}s`)

return timeSince.join(' ')
}
}

return (
<>
Expand Down
32 changes: 32 additions & 0 deletions bootstrap/manager/src/ui/src/settings/ConfigPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useState } from 'react'

function ConfigPanel () {
const [settings, setSettings] = useState({})

useEffect(() => {
fetch('/api/settings').then((res) => res.json()).then((settings) => {
setSettings(settings)
})
}, [])

return (
<>
<div className='bg-white p-6 rounded shadow-lg max-w-6xl w-full'>
<h3 className='text-3xl text-center'>Config</h3>
<h4 className='text-sm text-center text-gray-400 pb-4'>
This can be changed in the application.yaml on the server
</h4>
<div className='flex flex-col gap-4'>
{Object.keys(settings).map((key) => (
<div key={key} className='flex justify-between'>
<div className='font-bold'>{key}</div>
<div>{settings[key]}</div>
</div>
))}
</div>
</div>
</>
)
}

export default ConfigPanel
14 changes: 0 additions & 14 deletions bootstrap/manager/src/ui/src/settings/GeneralPanel.jsx

This file was deleted.

0 comments on commit 70dc3f0

Please sign in to comment.