Files
COMERCIAL-GTI/gti-maquina-comercial/launcher/Program.cs
T
GustavoandClaude Sonnet 5 f5af038e19 chore: import gti-maquina-comercial history via subtree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

git-subtree-dir: gti-maquina-comercial
git-subtree-mainline: 93ca82979e
git-subtree-split: b77484946f
2026-09-28 15:50:08 -03:00

199 lines
6.9 KiB
C#

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;
}
}
}