Skip to content

Commit dd0d3a4

Browse files
committed
feat(doctor): improve production diagnostics
2 parents 2f111f2 + bf1e5fe commit dd0d3a4

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

src/commands/DoctorCommand.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,88 @@ namespace vix::commands
968968
return line.substr(start, end - start);
969969
}
970970

971+
std::optional<std::string> detect_websocket_port_from_config()
972+
{
973+
const fs::path vixJson = fs::current_path() / "vix.json";
974+
975+
if (!fs::exists(vixJson))
976+
return std::nullopt;
977+
978+
try
979+
{
980+
const json root = read_json_or_throw(vixJson);
981+
982+
if (!root.is_object() ||
983+
!root.contains("production") ||
984+
!root["production"].is_object())
985+
{
986+
return std::nullopt;
987+
}
988+
989+
const auto &production = root["production"];
990+
991+
if (!production.contains("ports") ||
992+
!production["ports"].is_object())
993+
{
994+
return std::nullopt;
995+
}
996+
997+
const auto &ports = production["ports"];
998+
999+
if (!ports.contains("websocket") ||
1000+
!ports["websocket"].is_number_integer())
1001+
{
1002+
return std::nullopt;
1003+
}
1004+
1005+
const int port = ports["websocket"].get<int>();
1006+
1007+
if (port <= 0)
1008+
return std::nullopt;
1009+
1010+
return std::to_string(port);
1011+
}
1012+
catch (...)
1013+
{
1014+
return std::nullopt;
1015+
}
1016+
}
1017+
1018+
std::optional<std::string> detect_nginx_proxy_target(const std::string &projectName)
1019+
{
1020+
const std::string lower = lower_copy(projectName);
1021+
1022+
auto out = run_capture(
1023+
"grep -R \"proxy_pass\" /etc/nginx/sites-available /etc/nginx/sites-enabled "
1024+
"2>/dev/null | grep -i " +
1025+
shell_quote(lower) +
1026+
" | head -1");
1027+
1028+
if (!out)
1029+
{
1030+
out = run_capture(
1031+
"grep -R \"proxy_pass\" /etc/nginx/sites-available /etc/nginx/sites-enabled "
1032+
"2>/dev/null | head -1");
1033+
}
1034+
1035+
if (!out)
1036+
return std::nullopt;
1037+
1038+
const std::string line = *out;
1039+
const std::string key = "proxy_pass";
1040+
const auto pos = line.find(key);
1041+
1042+
if (pos == std::string::npos)
1043+
return std::nullopt;
1044+
1045+
std::string value = trim_copy(line.substr(pos + key.size()));
1046+
1047+
if (!value.empty() && value.back() == ';')
1048+
value.pop_back();
1049+
1050+
return trim_copy(value);
1051+
}
1052+
9711053
std::optional<std::string> detect_nginx_domain(const std::string &projectName)
9721054
{
9731055
const std::string lower = lower_copy(projectName);
@@ -1192,15 +1274,23 @@ namespace vix::commands
11921274
const auto workingDir =
11931275
serviceExists ? systemctl_property(*service, "WorkingDirectory") : std::nullopt;
11941276

1277+
const auto environment =
1278+
serviceExists ? systemctl_property(*service, "Environment") : std::nullopt;
1279+
11951280
const bool binaryExists = binary && fs::exists(*binary);
11961281
const bool running = binaryExists && process_running_for_binary(*binary);
11971282

11981283
const auto port =
11991284
binaryExists ? detect_listening_port_for_binary(*binary) : std::nullopt;
12001285

1286+
const auto websocketPort =
1287+
detect_websocket_port_from_config();
1288+
12011289
const bool nginxExists = nginx_config_exists_for_project(projectName);
12021290
const auto domain = detect_nginx_domain(projectName);
12031291
const bool tls = domain && tls_config_exists_for_domain(*domain);
1292+
const auto proxyTarget =
1293+
detect_nginx_proxy_target(projectName);
12041294

12051295
const bool localHealth = port && local_health_ok(*port);
12061296
const bool publicHealth = domain && public_health_ok(*domain);
@@ -1300,8 +1390,20 @@ namespace vix::commands
13001390
vix::cli::util::kv(std::cout, "Service status", serviceStatus.value_or("unknown"));
13011391
vix::cli::util::kv(std::cout, "Restart policy", restartPolicy.value_or("unknown"));
13021392
vix::cli::util::kv(std::cout, "Working directory", workingDir.value_or("unknown"));
1393+
vix::cli::util::kv(
1394+
std::cout,
1395+
"Environment",
1396+
environment && !environment->empty() ? *environment : "unknown");
13031397
vix::cli::util::kv(std::cout, "HTTP port", port.value_or("unknown"));
1398+
vix::cli::util::kv(
1399+
std::cout,
1400+
"WebSocket port",
1401+
websocketPort.value_or("unknown"));
13041402
vix::cli::util::kv(std::cout, "Proxy", nginxExists ? "nginx" : "not found");
1403+
vix::cli::util::kv(
1404+
std::cout,
1405+
"Proxy target",
1406+
proxyTarget.value_or("unknown"));
13051407
vix::cli::util::kv(std::cout, "Public URL", domain ? "https://" + *domain : "unknown");
13061408
vix::cli::util::kv(std::cout, "TLS", tls ? "enabled" : "unknown");
13071409
vix::cli::util::kv(std::cout, "Local health", localHealth ? "ok" : "unknown");
@@ -1378,6 +1480,9 @@ namespace vix::commands
13781480
out["websocket_health_configured"] = websocketHealthConfigured;
13791481
out["deploy_rollback_configured"] = deployRollbackConfigured;
13801482
out["readiness"] = json::array();
1483+
out["environment"] = environment.value_or("");
1484+
out["websocket_port"] = websocketPort.value_or("");
1485+
out["proxy_target"] = proxyTarget.value_or("");
13811486

13821487
for (const auto &item : readiness)
13831488
{

0 commit comments

Comments
 (0)