Skip to content

Commit

Permalink
Websocket support for Dashboard (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
ksmit799 authored Apr 8, 2024
1 parent 16220bc commit 0f67320
Show file tree
Hide file tree
Showing 34 changed files with 2,504 additions and 50 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "libs/yaml-cpp"]
path = libs/yaml-cpp
url = https://github.com/jbeder/yaml-cpp.git
[submodule "libs/json"]
path = libs/json
url = https://github.com/nlohmann/json.git
1 change: 1 addition & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)

# Include OpenSSL.
find_package(OpenSSL REQUIRED)
include_directories(${OPENSSL_INCLUDE_DIR})

# Include libuv.
add_subdirectory(libs/libuv)
include_directories(libs/libuv/include)

# Include uvw (libuv C++ wrapper).
include_directories(libs/uvw/src)

# Include JSON.
add_subdirectory(libs/json)
include_directories(libs/json/include)

# Include YAML-CPP.
add_subdirectory(libs/yaml-cpp)
include_directories(libs/yaml-cpp/include)
Expand Down Expand Up @@ -63,7 +71,7 @@ add_executable(ardos ${ARDOS_SOURCES} ${ARDOS_HEADERS})
add_subdirectory(libs/dclass)
include_directories(libs/dclass)

target_link_libraries(ardos PRIVATE uv yaml-cpp amqpcpp prometheus-cpp::pull)
target_link_libraries(ardos PRIVATE uv OpenSSL::SSL nlohmann_json::nlohmann_json yaml-cpp amqpcpp prometheus-cpp::pull)

if (ARDOS_WANT_DB_SERVER)
target_link_libraries(ardos PRIVATE mongo::mongocxx_shared mongo::bsoncxx_shared)
Expand Down
19 changes: 19 additions & 0 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ want-db-state-server: true
# Do we want metrics collection on this instance?
want-metrics: true

# Do we want a web interface running on this instance?
want-web-panel: true

# UberDOG definitions.
# Some example ones follow:
uberdogs:
Expand All @@ -36,6 +39,22 @@ uberdogs:
- id: 4667
class: FriendsManager

# Web Panel configuration.
# Can be accessed via the Ardos Web panel for debugging.
web-panel:
name: Ardos # The cluster name to appear in the dashboard.
port: 7781 # Port the WS connection listens on.

# Auth options.
# Make sure to change these in PROD environments.
username: ardos
password: ardos

# SSL/TLS config for web panel.
# The below two options can be omitted to disable SSL/TLS.
certificate: cert.pem
private-key: key.pem

# Metrics (Prometheus) configuration.
# This should be configured as a target in your Prometheus config.
metrics:
Expand Down
1 change: 1 addition & 0 deletions libs/json
Submodule json added at 9cca28
98 changes: 94 additions & 4 deletions src/clientagent/client_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../util/globals.h"
#include "../util/logger.h"
#include "../util/metrics.h"
#include "../web/web_panel.h"
#include "client_participant.h"

namespace Ardos {
Expand All @@ -27,7 +28,7 @@ ClientAgent::ClientAgent() {
_version = config["version"].as<std::string>();

// DC hash configuration.
// Can be manually overriden in CA config.
// Can be manually overridden in CA config.
_dcHash = g_dc_file->get_hash();
if (auto manualHash = config["manual-dc-hash"]) {
_dcHash = manualHash.as<uint32_t>();
Expand Down Expand Up @@ -123,8 +124,7 @@ ClientAgent::ClientAgent() {
srv.accept(*client);

// Create a new client for this connected participant.
// TODO: These should be tracked in a vector.
new ClientParticipant(this, client);
_participants.insert(new ClientParticipant(this, client));
});

// Initialize metrics.
Expand Down Expand Up @@ -259,10 +259,12 @@ void ClientAgent::ParticipantJoined() {
/**
* Called when a participant disconnects.
*/
void ClientAgent::ParticipantLeft() {
void ClientAgent::ParticipantLeft(ClientParticipant *client) {
if (_participantsGauge) {
_participantsGauge->Decrement();
}

_participants.erase(client);
}

/**
Expand Down Expand Up @@ -354,4 +356,92 @@ void ClientAgent::InitMetrics() {
_freeChannelsGauge->Set((double)(_channelsMax - _nextChannel));
}

void ClientAgent::HandleWeb(ws28::Client *client, nlohmann::json &data) {
if (data["msg"] == "init") {
// Build up an array of connected clients.
nlohmann::json clientInfo = nlohmann::json::array();
for (const auto &participant : _participants) {
clientInfo.push_back({
{"channel", std::to_string(participant->GetChannel())},
{"ip", participant->GetRemoteAddress().ip},
{"port", participant->GetRemoteAddress().port},
{"state", participant->GetAuthState()},
{"channels", participant->GetLocalChannels().size()},
{"postRemoves", participant->GetPostRemoves().size()},
});
}

WebPanel::Send(client, {
{"type", "ca:init"},
{"success", true},
{"listenIp", _host},
{"listenPort", _port},
#ifdef ARDOS_USE_LEGACY_CLIENT
{"legacy", true},
#else
{"legacy", false},
#endif
{"clients", clientInfo},
});
} else if (data["msg"] == "client") {
// We have to do this terribleness because JavaScript doesn't support
// uint64's.
auto channel = std::stoull(data["channel"].template get<std::string>());

// Try to find a matching client for the provided channel.
auto participant =
std::find_if(_participants.begin(), _participants.end(),
[&channel](ClientParticipant *participant) {
return participant->GetChannel() == channel;
});
if (participant == _participants.end()) {
WebPanel::Send(client, {
{"type", "ca:client"},
{"success", false},
});
return;
}

// Build an owned object array.
nlohmann::json ownedObjs = nlohmann::json::array();
for (const auto &obj : (*participant)->GetOwnedObjects()) {
ownedObjs.push_back({{"doId", obj.first},
{"clsName", obj.second.dcc->get_name()},
{"parent", obj.second.parent},
{"zone", obj.second.zone}});
}

// Build a session object array.
nlohmann::json sessionObjs = nlohmann::json::array();
for (const auto &doId : (*participant)->GetSessionObjects()) {
sessionObjs.push_back({{"doId", doId}});
}

// Build an active interests array.
nlohmann::json interests = nlohmann::json::array();
for (const auto &interest : (*participant)->GetInterests()) {
interests.push_back({{"id", interest.first},
{"parent", interest.second.parent},
{"zones", interest.second.zones}});
}

WebPanel::Send(
client,
{
{"type", "ca:client"},
{"success", true},
{"ip", (*participant)->GetRemoteAddress().ip},
{"port", (*participant)->GetRemoteAddress().port},
{"state", (*participant)->GetAuthState()},
{"channelHi", ((*participant)->GetChannel() >> 32) & 0xFFFFFFFF},
{"channelLo", (*participant)->GetChannel() & 0xFFFFFFFF},
{"channels", (*participant)->GetLocalChannels().size()},
{"postRemoves", (*participant)->GetPostRemoves().size()},
{"owned", ownedObjs},
{"session", sessionObjs},
{"interests", interests},
});
}
}

} // namespace Ardos
12 changes: 11 additions & 1 deletion src/clientagent/client_agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@

#include <memory>
#include <queue>
#include <unordered_set>

#include <dcClass.h>
#include <nlohmann/json.hpp>
#include <prometheus/counter.h>
#include <prometheus/gauge.h>
#include <prometheus/histogram.h>
#include <uvw.hpp>

#include "../net/ws/Client.h"

namespace Ardos {

struct Uberdog {
Expand All @@ -24,6 +28,8 @@ enum InterestsPermission {
INTERESTS_DISABLED,
};

class ClientParticipant;

class ClientAgent {
public:
ClientAgent();
Expand All @@ -43,11 +49,13 @@ class ClientAgent {
[[nodiscard]] unsigned long GetInterestTimeout() const;

void ParticipantJoined();
void ParticipantLeft();
void ParticipantLeft(ClientParticipant *client);
void RecordDatagram(const uint16_t &size);
void RecordInterestTimeout();
void RecordInterestTime(const double &seconds);

void HandleWeb(ws28::Client *client, nlohmann::json &data);

private:
void InitMetrics();

Expand All @@ -66,6 +74,8 @@ class ClientAgent {

std::unordered_map<uint32_t, Uberdog> _uberdogs;

std::unordered_set<ClientParticipant *> _participants;

uint64_t _nextChannel;
uint64_t _channelsMax;
std::queue<uint64_t> _freedChannels;
Expand Down
10 changes: 7 additions & 3 deletions src/clientagent/client_participant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ ClientParticipant::~ClientParticipant() {
// Call shutdown just in-case (most likely redundant.)
Shutdown();

_clientAgent->ParticipantLeft();
_clientAgent->ParticipantLeft(this);
}

/**
Expand Down Expand Up @@ -338,8 +338,9 @@ void ClientParticipant::HandleDatagram(const std::shared_ptr<Datagram> &dg) {
break;
}
case CLIENTAGENT_GET_NETWORK_ADDRESS: {
auto resp = std::make_shared<Datagram>(sender, _channel, CLIENTAGENT_GET_NETWORK_ADDRESS_RESP);
resp->AddUint32(dgi.GetUint32()); // Context.
auto resp = std::make_shared<Datagram>(
sender, _channel, CLIENTAGENT_GET_NETWORK_ADDRESS_RESP);
resp->AddUint32(dgi.GetUint32()); // Context.
resp->AddString(GetRemoteAddress().ip);
resp->AddUint16(GetRemoteAddress().port);
resp->AddString(GetLocalAddress().ip);
Expand Down Expand Up @@ -517,6 +518,9 @@ void ClientParticipant::HandleDatagram(const std::shared_ptr<Datagram> &dg) {
case STATESERVER_OBJECT_CHANGING_LOCATION: {
uint32_t doId = dgi.GetUint32();
if (TryQueuePending(doId, dgi.GetUnderlyingDatagram())) {
// The object that's changing location is currently generating inside an
// active InterestOperation. Queue this message to be handled after it
// generates.
return;
}

Expand Down
16 changes: 16 additions & 0 deletions src/clientagent/client_participant.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ class ClientParticipant final : public NetworkClient, public ChannelSubscriber {

friend class InterestOperation;

[[nodiscard]] uint64_t GetChannel() const { return _channel; }
[[nodiscard]] uint8_t GetAuthState() const { return _authState; }
[[nodiscard]] std::vector<std::shared_ptr<Datagram>> GetPostRemoves() const {
return _postRemoves;
}
[[nodiscard]] std::unordered_map<uint32_t, OwnedObject>
GetOwnedObjects() const {
return _ownedObjects;
}
[[nodiscard]] std::unordered_set<uint32_t> GetSessionObjects() const {
return _sessionObjects;
}
[[nodiscard]] std::unordered_map<uint16_t, Interest> GetInterests() const {
return _interests;
}

private:
void Shutdown() override;

Expand Down
15 changes: 13 additions & 2 deletions src/database/database_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "../util/globals.h"
#include "../util/logger.h"
#include "../util/metrics.h"
#include "../web/web_panel.h"
#include "database_utils.h"

// For document, finalize, et al.
Expand Down Expand Up @@ -528,8 +529,7 @@ void DatabaseServer::HandleGetField(DatagramIterator &dgi,
auto dbField = fields[field->get_name()];
if (dbField) {
// Pack the field into our object datagram.
DatabaseUtils::PackField(field, dbField.get_value(),
objectDg);
DatabaseUtils::PackField(field, dbField.get_value(), objectDg);
} else {
// Pack a default value.
objectDg.AddData(field->get_default_value());
Expand Down Expand Up @@ -1004,4 +1004,15 @@ void DatabaseServer::ReportFailed(const DatabaseServer::OperationType &type) {
}
}

void DatabaseServer::HandleWeb(ws28::Client *client, nlohmann::json &data) {
WebPanel::Send(client, {
{"type", "db"},
{"success", true},
{"host", _uri.to_string()},
{"channel", _channel},
{"minDoId", _minDoId},
{"maxDoId", _maxDoId},
});
}

} // namespace Ardos
4 changes: 4 additions & 0 deletions src/database/database_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@

#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <nlohmann/json.hpp>
#include <prometheus/counter.h>
#include <prometheus/histogram.h>
#include <uvw/timer.h>

#include "../messagedirector/channel_subscriber.h"
#include "../net/datagram_iterator.h"
#include "../net/message_types.h"
#include "../net/ws/Client.h"

namespace Ardos {

class DatabaseServer final : public ChannelSubscriber {
public:
DatabaseServer();

void HandleWeb(ws28::Client *client, nlohmann::json &data);

private:
void HandleDatagram(const std::shared_ptr<Datagram> &dg) override;

Expand Down
4 changes: 4 additions & 0 deletions src/messagedirector/channel_subscriber.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class ChannelSubscriber {
*/
void PublishDatagram(const std::shared_ptr<Datagram> &dg);

[[nodiscard]] std::vector<std::string> GetLocalChannels() const {
return _localChannels;
}

protected:
virtual void HandleDatagram(const std::shared_ptr<Datagram> &dg) = 0;

Expand Down
Loading

0 comments on commit 0f67320

Please sign in to comment.