Skip to content

Commit

Permalink
Add updating of client app_state_folder and settings
Browse files Browse the repository at this point in the history
  • Loading branch information
salty2011 committed Jan 27, 2025
1 parent 23e35ca commit b938c0c
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 1 deletion.
17 changes: 17 additions & 0 deletions src/moonlight-server/api/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ struct PairedClientsResponse {
std::vector<PairedClient> clients;
};

struct PartialClientSettings {
std::optional<uint> run_uid;
std::optional<uint> run_gid;
std::optional<std::vector<wolf::config::ControllerType>> controllers_override;
std::optional<float> mouse_acceleration;
std::optional<float> v_scroll_acceleration;
std::optional<float> h_scroll_acceleration;
};

struct UpdateClientSettingsRequest {
rfl::Description<"The client ID to identify the client (derived from certificate)", std::string> client_id;
rfl::Description<"New app state folder path (optional)", std::optional<std::string>> app_state_folder;
rfl::Description<"Client settings to update (only specified fields will be updated)", PartialClientSettings> settings;
};

struct AppListResponse {
bool success = true;
std::vector<rfl::Reflector<wolf::core::events::App>::ReflType> apps;
Expand Down Expand Up @@ -128,6 +143,8 @@ class UnixSocketServer {

void endpoint_RunnerStart(const HTTPRequest &req, std::shared_ptr<UnixSocket> socket);

void endpoint_UpdateClientSettings(const HTTPRequest &req, std::shared_ptr<UnixSocket> socket);

void sse_broadcast(const std::string &payload);
void sse_keepalive(const boost::system::error_code &e);

Expand Down
32 changes: 32 additions & 0 deletions src/moonlight-server/api/endpoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,36 @@ void UnixSocketServer::endpoint_RunnerStart(const wolf::api::HTTPRequest &req, s
}
}

void UnixSocketServer::endpoint_UpdateClientSettings(const HTTPRequest &req, std::shared_ptr<UnixSocket> socket) {
try {
auto payload_result = rfl::json::read<UpdateClientSettingsRequest>(req.body);
if (!payload_result) {
auto res = GenericErrorResponse{.error = "Invalid request format"};
send_http(socket, 400, rfl::json::write(res));
return;
}

const auto& payload = payload_result.value();

// Update the client settings
try {
state::update_client_settings(
this->state_->app_state->config,
payload.client_id.get(),
payload.app_state_folder.get(),
payload.settings.get()
);

auto res = GenericSuccessResponse{.success = true};
send_http(socket, 200, rfl::json::write(res));
} catch (const std::runtime_error& e) {
auto res = GenericErrorResponse{.error = e.what()};
send_http(socket, 404, rfl::json::write(res));
}
} catch (const std::exception &e) {
auto res = GenericErrorResponse{.error = e.what()};
send_http(socket, 500, rfl::json::write(res));
}
}

} // namespace wolf::api
15 changes: 15 additions & 0 deletions src/moonlight-server/api/unix_socket_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ UnixSocketServer::UnixSocketServer(boost::asio::io_context &io_context,
.handler = [this](auto req, auto socket) { endpoint_PairedClients(req, socket); },
});

state_->http.add(HTTPMethod::POST,
"/api/v1/clients/settings",
{
.summary = "Update client settings",
.description = "Update a client's settings including app state folder and client-specific settings",
.request_description = APIDescription{.json_schema = rfl::json::to_schema<UpdateClientSettingsRequest>()},
.response_description = {
{200, {.json_schema = rfl::json::to_schema<GenericSuccessResponse>()}},
{400, {.json_schema = rfl::json::to_schema<GenericErrorResponse>()}},
{404, {.json_schema = rfl::json::to_schema<GenericErrorResponse>()}},
{500, {.json_schema = rfl::json::to_schema<GenericErrorResponse>()}}
},
.handler = [this](auto req, auto socket) { endpoint_UpdateClientSettings(req, socket); },
});

/**
* Apps API
*/
Expand Down
8 changes: 7 additions & 1 deletion src/moonlight-server/state/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ inline std::size_t get_client_id(const PairedClient &current_client) {
return std::hash<std::string>{}(current_client.client_cert);
}

inline std::optional<PairedClient> get_client_by_id(const Config &cfg, std::size_t client_id) {
inline std::optional<PairedClient> get_client_by_id(const Config &cfg, const std::string &client_id) {
auto paired_clients = cfg.paired_clients->load();
auto search_result =
std::find_if(paired_clients->begin(), paired_clients->end(), [client_id](const PairedClient &pair_client) {
Expand Down Expand Up @@ -138,4 +138,10 @@ static moonlight::control::pkts::CONTROLLER_TYPE get_controller_type(const Contr
}
return moonlight::control::pkts::CONTROLLER_TYPE::AUTO;
}

std::optional<PairedClient> get_client_by_id(const Config &cfg, const std::string &client_id);
void update_client_settings(const Config &cfg,
const std::string &client_id,
const std::string &new_folder,
const wolf::config::ClientSettings &new_settings);
} // namespace state
73 changes: 73 additions & 0 deletions src/moonlight-server/state/configTOML.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,4 +305,77 @@ void unpair(const Config &cfg, const PairedClient &client) {
rfl::toml::save(cfg.config_source, tml);
}

std::optional<PairedClient> get_client_by_id(const Config &cfg, const std::string &client_id) {
auto paired_clients = cfg.paired_clients->load();
auto search_result = std::find_if(paired_clients->begin(),
paired_clients->end(),
[&client_id](const PairedClient &client) {
return client.app_state_folder == client_id;
});

if (search_result != paired_clients->end()) {
return *search_result;
}
return std::nullopt;
}

void update_client_settings(const Config &cfg,
const std::string &client_id,
const std::optional<std::string> &new_folder,
const PartialClientSettings &settings_update) {
// Get existing client first
auto client = get_client_by_id(cfg, client_id);
if (!client) {
throw std::runtime_error(fmt::format("Client with id {} not found", client_id));
}

// Merge new settings with existing ones
auto merged_settings = client->settings;
// Only update fields that are present in the update
if (settings_update.run_uid) merged_settings.run_uid = *settings_update.run_uid;
if (settings_update.run_gid) merged_settings.run_gid = *settings_update.run_gid;
if (settings_update.controllers_override) merged_settings.controllers_override = *settings_update.controllers_override;
if (settings_update.mouse_acceleration) merged_settings.mouse_acceleration = *settings_update.mouse_acceleration;
if (settings_update.v_scroll_acceleration) merged_settings.v_scroll_acceleration = *settings_update.v_scroll_acceleration;
if (settings_update.h_scroll_acceleration) merged_settings.h_scroll_acceleration = *settings_update.h_scroll_acceleration;

// Update the in-memory config atomically
cfg.paired_clients->update([&](const state::PairedClientList &paired_clients) {
return paired_clients
| ranges::views::transform([&](const auto &client) {
if (client->app_state_folder == client_id) {
return immer::box<PairedClient>(PairedClient{
.client_cert = client->client_cert,
.app_state_folder = new_folder.value_or(client->app_state_folder),
.settings = merged_settings
});
}
return client;
})
| ranges::to<state::PairedClientList>();
});

// Update the TOML file
auto toml_config = rfl::toml::load<WolfConfig, rfl::DefaultIfMissing>(cfg.config_source).value();

bool found = false;
for (auto &toml_client : toml_config.paired_clients) {
if (toml_client.app_state_folder == client_id) {
if (new_folder) {
toml_client.app_state_folder = *new_folder;
}
toml_client.settings = merged_settings;
found = true;
break;
}
}

if (!found) {
throw std::runtime_error(fmt::format("Client with id {} not found", client_id));
}

// Save back to file
rfl::toml::save(cfg.config_source, toml_config);
}

} // namespace state

0 comments on commit b938c0c

Please sign in to comment.