chore: import maquina-comercial-pessoal history via subtree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> git-subtree-dir: maquina-comercial-pessoal git-subtree-mainline:f5af038e19git-subtree-split:a9ef4d776d
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>Maquina-Comercial-Pessoal</AssemblyName>
|
||||
<RootNamespace>MaquinaComercialPessoalLauncher</RootNamespace>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
// Launcher da Maquina Comercial Pessoal:
|
||||
// 1. localiza a pasta do projeto (procura package.json + scripts/servidor.mjs);
|
||||
// 2. se o servidor ja responde em http://127.0.0.1:3910, 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 MaquinaComercialPessoalLauncher
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static void LoadDotEnv(string root)
|
||||
{
|
||||
string envPath = Path.Combine(root, ".env");
|
||||
if (!File.Exists(envPath)) return;
|
||||
foreach (string rawLine in File.ReadAllLines(envPath))
|
||||
{
|
||||
string line = rawLine.Trim();
|
||||
if (line.Length == 0 || line.StartsWith("#")) continue;
|
||||
int separator = line.IndexOf('=');
|
||||
if (separator <= 0) continue;
|
||||
string key = line.Substring(0, separator).Trim();
|
||||
string value = line.Substring(separator + 1).Trim();
|
||||
if (key.Length > 0 && Environment.GetEnvironmentVariable(key) == null)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string BaseUrl(string root)
|
||||
{
|
||||
LoadDotEnv(root);
|
||||
string port = Environment.GetEnvironmentVariable("APP_PORT");
|
||||
int parsedPort;
|
||||
if (!int.TryParse(port, out parsedPort) || parsedPort < 1 || parsedPort > 65535)
|
||||
{
|
||||
port = "3910";
|
||||
}
|
||||
else
|
||||
{
|
||||
port = parsedPort.ToString();
|
||||
}
|
||||
return "http://127.0.0.1:" + port;
|
||||
}
|
||||
|
||||
private static int Main()
|
||||
{
|
||||
Console.Title = "Maquina Comercial Pessoal";
|
||||
Console.WriteLine("=== Maquina Comercial Pessoal ===");
|
||||
|
||||
string root = LocateProjectDir();
|
||||
if (root == null)
|
||||
{
|
||||
return Fail("Pasta do projeto nao encontrada (procurei package.json + scripts/servidor.mjs).\n" +
|
||||
"Deixe o Maquina-Comercial-Pessoal.exe dentro da pasta do projeto.");
|
||||
}
|
||||
|
||||
string url = BaseUrl(root);
|
||||
|
||||
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())
|
||||
{
|
||||
if ((int)response.StatusCode != 200) return false;
|
||||
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
string body = reader.ReadToEnd();
|
||||
string compact = body.Replace(" ", "").Replace("\r", "").Replace("\n", "").Replace("\t", "");
|
||||
return (int)response.StatusCode == 200 &&
|
||||
compact.IndexOf("\"app\":\"maquina-comercial-pessoal\"", StringComparison.Ordinal) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user