Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> git-subtree-dir: gti-maquina-comercial git-subtree-mainline:93ca82979egit-subtree-split:b77484946f
199 lines
6.9 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|