using System; using System.Diagnostics; using System.IO; using System.Net; using System.Threading; // Launcher do GTI Maquina Comercial: // 1. localiza a pasta do projeto (procura package.json + scripts/servidor.mjs); // 2. se o servidor ja responde em http://127.0.0.1:3900, so abre o navegador; // 3. senao, inicia "node scripts/servidor.mjs" escondido (log em data/server.log), // espera o /api/health responder (ate 30s) e abre o navegador. // // Compilavel tanto pelo dotnet SDK (net8.0) quanto pelo csc.exe do // .NET Framework 4 (C# 5): sem interpolacao de string, sem APIs novas. namespace GtiComercialLauncher { internal static class Program { private static string BaseUrl() { string port = Environment.GetEnvironmentVariable("GTI_PORT"); if (string.IsNullOrEmpty(port)) port = "3900"; return "http://127.0.0.1:" + port; } private static int Main() { Console.Title = "GTI Maquina Comercial"; Console.WriteLine("=== GTI Maquina Comercial ==="); string root = LocateProjectDir(); if (root == null) { return Fail("Pasta do projeto nao encontrada (procurei package.json + scripts/servidor.mjs).\n" + "Deixe o GTI-Comercial.exe dentro da pasta do projeto."); } string url = BaseUrl(); if (IsHealthy(url)) { Console.WriteLine("Servidor ja esta rodando. Abrindo o navegador..."); OpenBrowser(url); return 0; } string node = FindNode(); if (node == null) { return Fail("Node.js nao encontrado no PATH. Instale em https://nodejs.org (versao 24 ou superior)."); } Console.WriteLine("Iniciando o servidor (aguarde alguns segundos)..."); if (!StartServer(node, root)) { return Fail("Nao foi possivel iniciar o servidor. Veja o log em data\\server.log"); } for (int i = 0; i < 60; i++) { Thread.Sleep(500); if (IsHealthy(url)) { Console.WriteLine("Pronto! Abrindo " + url + " no navegador..."); OpenBrowser(url); Thread.Sleep(1500); return 0; } } return Fail("O servidor nao respondeu em 30 segundos.\n" + "Veja o log em data\\server.log ou rode manualmente: node scripts\\servidor.mjs"); } private static string LocateProjectDir() { string[] candidates = new string[] { AppDomain.CurrentDomain.BaseDirectory, Directory.GetCurrentDirectory() }; foreach (string start in candidates) { DirectoryInfo dir = new DirectoryInfo(start); for (int depth = 0; dir != null && depth < 5; depth++) { if (File.Exists(Path.Combine(dir.FullName, "package.json")) && File.Exists(Path.Combine(Path.Combine(dir.FullName, "scripts"), "servidor.mjs"))) { return dir.FullName; } dir = dir.Parent; } } return null; } private static string FindNode() { try { ProcessStartInfo psi = new ProcessStartInfo("where.exe", "node"); psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.CreateNoWindow = true; using (Process p = Process.Start(psi)) { string output = p.StandardOutput.ReadToEnd(); p.WaitForExit(5000); if (!string.IsNullOrEmpty(output)) { string first = output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)[0].Trim(); if (File.Exists(first)) return first; } } } catch (Exception) { // continua para o retorno nulo } return null; } private static bool StartServer(string node, string root) { try { string dataDir = Path.Combine(root, "data"); Directory.CreateDirectory(dataDir); string logPath = Path.Combine(dataDir, "server.log"); File.AppendAllText(logPath, "---- " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " launcher iniciou o servidor ----\r\n"); // O redirecionamento para o log e feito pelo proprio cmd.exe: assim o // launcher pode encerrar sem deixar o servidor escrevendo num pipe // sem leitor (o que derrubaria o node com EPIPE mais tarde). string comando = "/c \"\"" + node + "\" scripts\\servidor.mjs >> \"" + logPath + "\" 2>&1\""; ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", comando); psi.WorkingDirectory = root; psi.UseShellExecute = false; psi.CreateNoWindow = true; Process.Start(psi); return true; } catch (Exception ex) { Console.WriteLine("Falha ao iniciar: " + ex.Message); return false; } } private static bool IsHealthy(string baseUrl) { try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(baseUrl + "/api/health"); request.Timeout = 1500; request.Method = "GET"; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { return (int)response.StatusCode == 200; } } catch (Exception) { return false; } } private static void OpenBrowser(string url) { try { ProcessStartInfo psi = new ProcessStartInfo(url); psi.UseShellExecute = true; Process.Start(psi); } catch (Exception) { Console.WriteLine("Abra manualmente no navegador: " + url); } } private static int Fail(string message) { Console.WriteLine(); Console.WriteLine("ERRO: " + message); Console.WriteLine(); Console.WriteLine("Pressione qualquer tecla para fechar..."); try { Console.ReadKey(true); } catch (Exception) { Thread.Sleep(8000); } return 1; } } }