Add UUID to apps w/ add option to use App's id for virtual display (resolves #48)
This commit is contained in:
@@ -395,38 +395,8 @@ namespace confighttp {
|
||||
pt::read_json(ss, inputTree);
|
||||
pt::read_json(config::stream.file_apps, fileTree);
|
||||
|
||||
if (inputTree.get_child("prep-cmd").empty()) {
|
||||
inputTree.erase("prep-cmd");
|
||||
}
|
||||
proc::migrate_apps(&fileTree, &inputTree);
|
||||
|
||||
if (inputTree.get_child("detached").empty()) {
|
||||
inputTree.erase("detached");
|
||||
}
|
||||
|
||||
auto &apps_node = fileTree.get_child("apps"s);
|
||||
int index = inputTree.get<int>("index");
|
||||
|
||||
inputTree.erase("index");
|
||||
|
||||
if (index == -1) {
|
||||
apps_node.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
else {
|
||||
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
||||
pt::ptree newApps;
|
||||
int i = 0;
|
||||
for (const auto &kv : apps_node) {
|
||||
if (i == index) {
|
||||
newApps.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
else {
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
fileTree.erase("apps");
|
||||
fileTree.push_back(std::make_pair("apps", newApps));
|
||||
}
|
||||
pt::write_json(config::stream.file_apps, fileTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
@@ -450,33 +420,38 @@ namespace confighttp {
|
||||
pt::ptree outputTree;
|
||||
auto g = util::fail_guard([&]() {
|
||||
std::ostringstream data;
|
||||
|
||||
pt::write_json(data, outputTree);
|
||||
response->write(data.str());
|
||||
});
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
if (
|
||||
args.find("uuid"s) == std::end(args)
|
||||
) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Missing a required launch parameter");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto uuid = nvhttp::get_arg(args, "uuid");
|
||||
|
||||
pt::ptree fileTree;
|
||||
try {
|
||||
pt::read_json(config::stream.file_apps, fileTree);
|
||||
auto &apps_node = fileTree.get_child("apps"s);
|
||||
int index = stoi(request->path_match[1]);
|
||||
|
||||
if (index < 0) {
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid Index");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
||||
pt::ptree newApps;
|
||||
int i = 0;
|
||||
for (const auto &kv : apps_node) {
|
||||
if (i++ != index) {
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
}
|
||||
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
||||
pt::ptree newApps;
|
||||
for (const auto &kv : apps_node) {
|
||||
auto app_uuid = kv.second.get_optional<std::string>("uuid"s);
|
||||
if (!app_uuid || app_uuid.value() != uuid) {
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
}
|
||||
fileTree.erase("apps");
|
||||
fileTree.push_back(std::make_pair("apps", newApps));
|
||||
}
|
||||
fileTree.erase("apps");
|
||||
fileTree.push_back(std::make_pair("apps", newApps));
|
||||
|
||||
pt::write_json(config::stream.file_apps, fileTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
@@ -951,7 +926,7 @@ namespace confighttp {
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
if (
|
||||
args.find("id"s) == std::end(args)
|
||||
args.find("uuid"s) == std::end(args)
|
||||
) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Missing a required launch parameter");
|
||||
@@ -959,41 +934,42 @@ namespace confighttp {
|
||||
return;
|
||||
}
|
||||
|
||||
auto idx_str = nvhttp::get_arg(args, "id");
|
||||
auto idx = util::from_view(idx_str);
|
||||
auto uuid = nvhttp::get_arg(args, "uuid");
|
||||
|
||||
const auto& apps = proc::proc.get_apps();
|
||||
|
||||
if (idx >= apps.size()) {
|
||||
BOOST_LOG(error) << "Couldn't find app with index ["sv << idx_str << ']';
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Cannot find requested application");
|
||||
return;
|
||||
for (auto& app : apps) {
|
||||
if (app.uuid == uuid) {
|
||||
auto appid = util::from_view(app.id);
|
||||
|
||||
crypto::named_cert_t named_cert {
|
||||
.name = "",
|
||||
.uuid = http::unique_id,
|
||||
.perm = crypto::PERM::_all,
|
||||
};
|
||||
|
||||
BOOST_LOG(info) << "Launching app ["sv << app.name << "] from web UI"sv;
|
||||
|
||||
auto launch_session = nvhttp::make_launch_session(true, appid, args, &named_cert);
|
||||
auto err = proc::proc.execute(appid, app, launch_session);
|
||||
if (err) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error",
|
||||
err == 503
|
||||
? "Failed to initialize video capture/encoding. Is a display connected and turned on?"
|
||||
: "Failed to start the specified application");
|
||||
return;
|
||||
} else {
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto& app = apps[idx];
|
||||
auto appid = util::from_view(app.id);
|
||||
|
||||
crypto::named_cert_t named_cert {
|
||||
.name = "",
|
||||
.uuid = http::unique_id,
|
||||
.perm = crypto::PERM::_all,
|
||||
};
|
||||
|
||||
BOOST_LOG(info) << "Launching app ["sv << app.name << "] from web UI"sv;
|
||||
|
||||
auto launch_session = nvhttp::make_launch_session(true, appid, args, &named_cert);
|
||||
auto err = proc::proc.execute(appid, app, launch_session);
|
||||
if (err) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error",
|
||||
err == 503
|
||||
? "Failed to initialize video capture/encoding. Is a display connected and turned on?"
|
||||
: "Failed to start the specified application");
|
||||
return;
|
||||
} else {
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
BOOST_LOG(error) << "Couldn't find app with uuid ["sv << uuid << ']';
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Cannot find requested application");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1086,22 +1062,22 @@ namespace confighttp {
|
||||
server.resource["^/api/pin$"]["POST"] = savePin;
|
||||
server.resource["^/api/otp$"]["GET"] = getOTP;
|
||||
server.resource["^/api/apps$"]["GET"] = getApps;
|
||||
server.resource["^/api/logs$"]["GET"] = getLogs;
|
||||
server.resource["^/api/apps$"]["POST"] = saveApp;
|
||||
server.resource["^/api/apps/delete$"]["POST"] = deleteApp;
|
||||
server.resource["^/api/apps/launch$"]["POST"] = launchApp;
|
||||
server.resource["^/api/apps/close$"]["POST"] = closeApp;
|
||||
server.resource["^/api/logs$"]["GET"] = getLogs;
|
||||
server.resource["^/api/config$"]["GET"] = getConfig;
|
||||
server.resource["^/api/config$"]["POST"] = saveConfig;
|
||||
server.resource["^/api/configLocale$"]["GET"] = getLocale;
|
||||
server.resource["^/api/restart$"]["POST"] = restart;
|
||||
server.resource["^/api/quit$"]["POST"] = quit;
|
||||
server.resource["^/api/password$"]["POST"] = savePassword;
|
||||
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
|
||||
server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll;
|
||||
server.resource["^/api/clients/list$"]["GET"] = listClients;
|
||||
server.resource["^/api/clients/update$"]["POST"] = updateClient;
|
||||
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
|
||||
server.resource["^/api/clients/disconnect$"]["POST"] = disconnect;
|
||||
server.resource["^/api/apps/launch$"]["POST"] = launchApp;
|
||||
server.resource["^/api/apps/close$"]["POST"] = closeApp;
|
||||
server.resource["^/api/covers/upload$"]["POST"] = uploadCover;
|
||||
server.resource["^/images/apollo.ico$"]["GET"] = getFaviconImage;
|
||||
server.resource["^/images/logo-apollo-45.png$"]["GET"] = getSunshineLogoImage;
|
||||
|
||||
@@ -211,13 +211,16 @@ namespace proc {
|
||||
VDISPLAY::setRenderAdapterByName(platf::from_utf8(config::video.adapter_name));
|
||||
}
|
||||
|
||||
auto device_uuid = uuid_util::uuid_t::parse(launch_session->unique_id);
|
||||
auto device_uuid_str = _app.use_app_identity ? _app.uuid : launch_session->unique_id;
|
||||
auto device_name = _app.use_app_identity ? _app.name : launch_session->device_name;
|
||||
|
||||
auto device_uuid = uuid_util::uuid_t::parse(device_uuid_str);
|
||||
|
||||
memcpy(&launch_session->display_guid, &device_uuid, sizeof(GUID));
|
||||
|
||||
std::wstring vdisplayName = VDISPLAY::createVirtualDisplay(
|
||||
launch_session->unique_id.c_str(),
|
||||
launch_session->device_name.c_str(),
|
||||
device_uuid_str.c_str(),
|
||||
device_name.c_str(),
|
||||
render_width,
|
||||
render_height,
|
||||
launch_session->fps ? launch_session->fps : 60,
|
||||
@@ -794,6 +797,63 @@ namespace proc {
|
||||
return std::make_tuple(id_no_index, id_with_index);
|
||||
}
|
||||
|
||||
void
|
||||
migrate_apps(pt::ptree* fileTree_p, pt::ptree* inputTree_p) {
|
||||
std::string new_app_uuid;
|
||||
|
||||
if (inputTree_p) {
|
||||
auto inputTree = *inputTree_p;
|
||||
auto input_uuid = inputTree_p->get_optional<std::string>("uuid"s);
|
||||
if (input_uuid && !input_uuid.value().empty()) {
|
||||
new_app_uuid = input_uuid.value();
|
||||
} else {
|
||||
new_app_uuid = uuid_util::uuid_t::generate().string();
|
||||
inputTree_p->erase("uuid");
|
||||
inputTree_p->put("uuid", new_app_uuid);
|
||||
}
|
||||
|
||||
if (inputTree_p->get_child("prep-cmd").empty()) {
|
||||
inputTree_p->erase("prep-cmd");
|
||||
}
|
||||
|
||||
if (inputTree_p->get_child("detached").empty()) {
|
||||
inputTree_p->erase("detached");
|
||||
}
|
||||
|
||||
inputTree_p->erase("launching");
|
||||
inputTree_p->erase("index");
|
||||
}
|
||||
|
||||
auto &apps_node = fileTree_p->get_child("apps"s);
|
||||
|
||||
pt::ptree newApps;
|
||||
for (auto &kv : apps_node) {
|
||||
// Check if we have apps that have not got an uuid assigned
|
||||
auto app_uuid = kv.second.get_optional<std::string>("uuid"s);
|
||||
if (!app_uuid || app_uuid.value().empty()) {
|
||||
kv.second.erase("uuid");
|
||||
kv.second.put("uuid", uuid_util::uuid_t::generate().string());
|
||||
kv.second.erase("launching");
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
} else {
|
||||
if (!new_app_uuid.empty() && app_uuid.value() == new_app_uuid) {
|
||||
newApps.push_back(std::make_pair("", *inputTree_p));
|
||||
new_app_uuid.clear();
|
||||
} else {
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally add the new app
|
||||
if (!new_app_uuid.empty()) {
|
||||
newApps.push_back(std::make_pair("", *inputTree_p));
|
||||
}
|
||||
|
||||
fileTree_p->erase("apps");
|
||||
fileTree_p->push_back(std::make_pair("apps", newApps));
|
||||
}
|
||||
|
||||
std::optional<proc::proc_t>
|
||||
parse(const std::string &file_name) {
|
||||
pt::ptree tree;
|
||||
@@ -816,6 +876,20 @@ namespace proc {
|
||||
for (auto &[_, app_node] : apps_node) {
|
||||
proc::ctx_t ctx;
|
||||
|
||||
auto app_uuid = app_node.get_optional<std::string>("uuid"s);
|
||||
|
||||
if (!app_uuid) {
|
||||
// We need an upgrade to the app list
|
||||
try {
|
||||
migrate_apps(&tree, nullptr);
|
||||
pt::write_json(file_name, tree);
|
||||
return parse(file_name);
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "Error happened wilie migrating the app list: "sv << e.what();
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s);
|
||||
auto detached_nodes_opt = app_node.get_child_optional("detached"s);
|
||||
auto exclude_global_prep = app_node.get_optional<bool>("exclude-global-prep-cmd"s);
|
||||
@@ -831,6 +905,9 @@ namespace proc {
|
||||
auto virtual_display = app_node.get_optional<bool>("virtual-display"s);
|
||||
auto virtual_display_primary = app_node.get_optional<bool>("virtual-display-primary"s);
|
||||
auto resolution_scale_factor = app_node.get_optional<int>("scale-factor"s);
|
||||
auto use_app_identity = app_node.get_optional<bool>("use-app-identity"s);
|
||||
|
||||
ctx.uuid = app_uuid.value();
|
||||
|
||||
std::vector<proc::cmd_t> prep_cmds;
|
||||
if (!exclude_global_prep.value_or(false)) {
|
||||
@@ -901,6 +978,7 @@ namespace proc {
|
||||
ctx.virtual_display = virtual_display.value_or(false);
|
||||
ctx.virtual_display_primary = virtual_display_primary.value_or(true);
|
||||
ctx.scale_factor = resolution_scale_factor.value_or(100);
|
||||
ctx.use_app_identity = use_app_identity.value_or(false);
|
||||
|
||||
auto possible_ids = calculate_app_id(name, ctx.image_path, i++);
|
||||
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
||||
@@ -923,11 +1001,13 @@ namespace proc {
|
||||
#ifdef _WIN32
|
||||
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK) {
|
||||
proc::ctx_t ctx;
|
||||
// ctx.uuid = ""; // We're not using uuid for this special entry
|
||||
ctx.name = "Virtual Display";
|
||||
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
|
||||
ctx.virtual_display = true;
|
||||
ctx.virtual_display_primary = true;
|
||||
ctx.scale_factor = 100;
|
||||
ctx.use_app_identity = false;
|
||||
|
||||
ctx.elevated = false;
|
||||
ctx.auto_detach = true;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "platform/common.h"
|
||||
@@ -59,6 +60,7 @@ namespace proc {
|
||||
*/
|
||||
std::vector<std::string> detached;
|
||||
|
||||
std::string uuid;
|
||||
std::string name;
|
||||
std::string cmd;
|
||||
std::string working_dir;
|
||||
@@ -70,6 +72,7 @@ namespace proc {
|
||||
bool wait_all;
|
||||
bool virtual_display;
|
||||
bool virtual_display_primary;
|
||||
bool use_app_identity;
|
||||
int scale_factor;
|
||||
std::chrono::seconds exit_timeout;
|
||||
};
|
||||
@@ -150,6 +153,8 @@ namespace proc {
|
||||
validate_app_image_path(std::string app_image_path);
|
||||
void
|
||||
refresh(const std::string &file_name);
|
||||
void
|
||||
migrate_apps(boost::property_tree::ptree* fileTree_p, boost::property_tree::ptree* inputTree_p);
|
||||
std::optional<proc::proc_t>
|
||||
parse(const std::string &file_name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user