@purecore/one-server-4-all 0.1.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/COMPARACAO_LIVE_SERVER.md +37 -35
- package/README.md +44 -26
- package/dist/cert-generator.js +62 -44
- package/dist/deployer.js +10 -11
- package/dist/index.js +1 -1
- package/dist/server.js +8 -8
- package/hot-server.deps.graflow +1 -1
- package/package.json +3 -2
- package/reports/21-12-2025_04-21.md +25 -4
- package/reports/31-12-2025_21-15.md +2 -2
- package/reports/31-12-2025_21-45.md +1 -1
- package/reports/31-12-2025_22-00.md +1 -1
- package/src/cert-generator.ts +196 -158
- package/src/deployer.ts +11 -12
- package/src/index.ts +1 -1
- package/src/server.ts +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
# Release v0.5.2
|
|
2
|
+
|
|
3
|
+
### What's Changed
|
|
4
|
+
|
|
5
|
+
- 🚑 Alterada porta padrão de `6000` para `8080` (fix ERR_UNSAFE_PORT em browsers)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Release v0.5.1
|
|
10
|
+
|
|
11
|
+
### What's Changed
|
|
12
|
+
|
|
13
|
+
- 🐛 Correção no comando PM2 gerado para usar `npx one-server-4-all` em vez de caminho local
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
1
17
|
# Release v0.5.0
|
|
2
18
|
|
|
3
19
|
### What's Changed
|
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
# Comparativo: Purecore Hot Server vs Live Server
|
|
2
2
|
|
|
3
|
-
Este documento analisa as diferenças técnicas e de funcionalidades entre a **Sua biblioteca (`purecore-
|
|
3
|
+
Este documento analisa as diferenças técnicas e de funcionalidades entre a **Sua biblioteca (`purecore-one-server-4-all`)** e a biblioteca padrão de mercado **[`live-server`](https://github.com/tapio/live-server)**.
|
|
4
4
|
|
|
5
5
|
## 📊 Visão Geral
|
|
6
6
|
|
|
7
|
-
| Feature
|
|
8
|
-
|
|
|
9
|
-
| **Dependências**
|
|
10
|
-
| **Linguagem**
|
|
11
|
-
| **Hot Reload**
|
|
12
|
-
| **Watch System**
|
|
13
|
-
| **SPA Support**
|
|
14
|
-
| **Directory Listing**
|
|
15
|
-
| **HTTPS/Proxy**
|
|
16
|
-
| **Middleware**
|
|
17
|
-
| **Extensão `.html` opcional**
|
|
18
|
-
| **Compatível com `npx`/`npm i -g`** | Sim
|
|
7
|
+
| Feature | `live-server` (Tapio) | `purecore-one-server-4-all` (Sua Lib) |
|
|
8
|
+
| :---------------------------------- | :-------------------------------------------------- | :---------------------------------------------------------------- |
|
|
9
|
+
| **Dependências** | Múltiplas (`send`, `fsevents`, `opn`, `connect`...) | **Zero (0)** (Apenas nativas do Node.js) |
|
|
10
|
+
| **Linguagem** | JavaScript | **TypeScript** |
|
|
11
|
+
| **Hot Reload** | Full Page + **CSS Injection** (sem refresh) | Full Page + **CSS Injection** (sem refresh) |
|
|
12
|
+
| **Watch System** | `chokidar` (geralmente) | `fs.watch` nativo (recursivo) |
|
|
13
|
+
| **SPA Support** | Sim (redireciona 404 para index.html) | Sim (flag `--spa=true`) |
|
|
14
|
+
| **Directory Listing** | Sim (mostra arquivos se não houver index) | Não (retorna erro/404) |
|
|
15
|
+
| **HTTPS/Proxy** | Sim | HTTPS: Sim (flag `--https=true`) / Proxy: Não |
|
|
16
|
+
| **Middleware** | Sim (baseado em Connect) | Não |
|
|
17
|
+
| **Extensão `.html` opcional** | Não (depende do arquivo/rota) | Sim (fallback para servir `.html` quando a URL vier sem extensão) |
|
|
18
|
+
| **Compatível com `npx`/`npm i -g`** | Sim | Sim (via build `dist/` e campo `bin` no `package.json`) |
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
22
|
## ✅ O que a sua lib tem de Diferencial (Vantagens)
|
|
23
23
|
|
|
24
24
|
1. **Zero Dependencies (Zero Dependências)**:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
- **Segurança/Auditabilidade**: Ao não usar dependências de terceiros, você elimina riscos de _supply chain attacks_ e bloatware.
|
|
27
|
+
- **Instalação Instantânea**: `npm install` roda em milissegundos.
|
|
28
|
+
- **Tamanho**: O projeto final é minúsculo comparado ao `live-server` e suas árvores de dependência.
|
|
28
29
|
|
|
29
30
|
2. **Base de Código Moderna (TypeScript + Node 20+)**:
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
- O código utiliza APIs modernas como `node:fs/promises`, `node:watch` (recursivo) e Typescript estrito.
|
|
33
|
+
- É muito mais fácil para um desenvolvedor TS ler, entender e modificar o seu código do que o código legado JS do `live-server`.
|
|
32
34
|
|
|
33
35
|
3. **Simplicidade Arquitetural**:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
- Sua implementação de SSE (Server-Sent Events) é direta e transparente (`/_hot_server_sse`), sem dependência de bibliotecas complexas de socket.
|
|
37
|
+
- Validação "Zod-like" interna (`validator.ts`) demonstra como fazer type-safety sem bibliotecas pesadas.
|
|
36
38
|
|
|
37
39
|
---
|
|
38
40
|
|
|
@@ -42,36 +44,36 @@ Para igualar a funcionalidade, você precisaria implementar:
|
|
|
42
44
|
|
|
43
45
|
### 1. Injeção de CSS (CSS Hot Loading)
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
- **O que é**: Quando um arquivo `.css` é salvo, o `live-server` atualiza apenas o estilo na página sem recarregar o navegador.
|
|
48
|
+
- **Seu estado atual**: Implementado via SSE + troca de `href` com timestamp.
|
|
49
|
+
- **Como implementar**: No script injetado, verificar se a mensagem do SSE é sobre um arquivo CSS e, nesse caso, buscar as tags `<link rel="stylesheet">` no DOM e forçar uma atualização do `href` (ex: `style.css?v=timestamp`) em vez de dar reload.
|
|
48
50
|
|
|
49
51
|
### 2. Suporte a SPA (Single Page Applications)
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
- **O que é**: Frameworks como React/Vue (via Router) precisam que qualquer rota desconhecida (ex: `/usuarios/1`) retorne o `index.html` para que o JS no front assuma o controle.
|
|
54
|
+
- **Seu estado atual**: Implementado via flag `--spa=true` (fallback para `index.html` quando o arquivo não existe).
|
|
53
55
|
|
|
54
56
|
### 3. Mime-Types Robustos
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
- **Seu estado atual**: Implementado com lista expandida de tipos (vídeos, fontes, manifestos, etc).
|
|
57
59
|
|
|
58
60
|
### 4. CORS
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
- **Seu estado atual**: Implementado com headers `Access-Control-Allow-*` nas respostas.
|
|
61
63
|
|
|
62
64
|
### 5. Directory e Range Requests
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
- **O que falta**:
|
|
67
|
+
- **Listagem de pasta**: O `live-server` gera uma interface HTML listando os arquivos se você abrir uma pasta. O seu tenta abrir `index.html` e falha se não existir.
|
|
68
|
+
- **Range Requests**: Para fazer streaming de vídeo/áudio e permitir "pular" (seek) o vídeo, o servidor precisa suportar headers `Range` e `Content-Range`. O seu `createReadStream.pipe(res)` serve o arquivo inteiro, o que quebra alguns players de vídeo.
|
|
67
69
|
|
|
68
70
|
---
|
|
69
71
|
|
|
70
72
|
## 🔒 HTTPS (nota prática no WSL)
|
|
71
73
|
|
|
72
|
-
O `purecore-
|
|
74
|
+
O `purecore-one-server-4-all` suporta HTTPS com certificados auto-assinados para desenvolvimento local.
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
- **Gerar/limpar/inspecionar certs**:
|
|
75
77
|
|
|
76
78
|
```bash
|
|
77
79
|
bun run certs:clean
|
|
@@ -79,7 +81,7 @@ bun run certs:generate
|
|
|
79
81
|
bun run certs:info
|
|
80
82
|
```
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
- **Iniciar em HTTPS**:
|
|
83
85
|
|
|
84
86
|
```bash
|
|
85
87
|
bun run dev:https
|
|
@@ -89,8 +91,8 @@ bun run src/index.ts --https=true
|
|
|
89
91
|
|
|
90
92
|
**Referências**:
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
- Campo `bin` do npm (execução via `npx`/instalação global): `https://docs.npmjs.com/cli/v10/configuring-npm/package-json#bin`
|
|
95
|
+
- Node.js ESM/CJS (impacta como o `dist/` roda no Node): `https://nodejs.org/api/packages.html`
|
|
94
96
|
|
|
95
97
|
---
|
|
96
98
|
|
package/README.md
CHANGED
|
@@ -16,9 +16,9 @@ Um servidor de desenvolvimento hot-reload moderno e leve, construído com TypeSc
|
|
|
16
16
|
## 🚀 Instalação
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install -g
|
|
19
|
+
npm install -g one-server-4-all
|
|
20
20
|
# ou
|
|
21
|
-
bun install -g
|
|
21
|
+
bun install -g one-server-4-all
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
## 🔒 Modo HTTPS
|
|
@@ -26,9 +26,10 @@ bun install -g hot-server
|
|
|
26
26
|
O Hot Server suporta HTTPS com certificados auto-assinados para desenvolvimento local.
|
|
27
27
|
|
|
28
28
|
### Ativar HTTPS
|
|
29
|
+
|
|
29
30
|
```bash
|
|
30
31
|
# Via linha de comando
|
|
31
|
-
|
|
32
|
+
one-server-4-all --https=true
|
|
32
33
|
|
|
33
34
|
# Via npm scripts
|
|
34
35
|
npm run dev:https
|
|
@@ -38,6 +39,7 @@ bun run dev:https
|
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
### Gerenciamento de Certificados
|
|
42
|
+
|
|
41
43
|
```bash
|
|
42
44
|
# Gerar certificados auto-assinados
|
|
43
45
|
npm run certs:generate
|
|
@@ -49,30 +51,34 @@ npm run certs:info
|
|
|
49
51
|
npm run certs:clean
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
**Nota**: Os certificados são salvos em `.
|
|
54
|
+
**Nota**: Os certificados são salvos em `.one-server-4-all-certs/` no diretório do projeto.
|
|
53
55
|
|
|
54
56
|
## 📖 Uso Básico
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
59
|
# Na pasta do seu projeto
|
|
58
|
-
|
|
60
|
+
one-server-4-all
|
|
59
61
|
|
|
60
62
|
# Com opções
|
|
61
|
-
|
|
63
|
+
one-server-4-all --port=3000 --spa=true
|
|
62
64
|
```
|
|
63
65
|
|
|
64
66
|
## 🎯 Funcionalidades Avançadas
|
|
65
67
|
|
|
66
68
|
### CSS Hot Loading
|
|
69
|
+
|
|
67
70
|
Quando você modifica arquivos `.css`, apenas o estilo é atualizado sem recarregar a página inteira.
|
|
68
71
|
|
|
69
72
|
### SPA Support
|
|
73
|
+
|
|
70
74
|
```bash
|
|
71
|
-
|
|
75
|
+
one-server-4-all --spa=true
|
|
72
76
|
```
|
|
77
|
+
|
|
73
78
|
Rotas inexistentes (como `/usuarios/1`) automaticamente servem `index.html`, permitindo que seu framework frontend assuma o roteamento.
|
|
74
79
|
|
|
75
80
|
### MIME Types Suportados
|
|
81
|
+
|
|
76
82
|
- **Vídeos**: MP4, WebM, OGG, AVI, MOV, WMV, FLV
|
|
77
83
|
- **Áudios**: MP3, WAV, OGG, AAC, M4A, Opus
|
|
78
84
|
- **Fontes**: WOFF, WOFF2, TTF, OTF, EOT
|
|
@@ -82,19 +88,20 @@ Rotas inexistentes (como `/usuarios/1`) automaticamente servem `index.html`, per
|
|
|
82
88
|
|
|
83
89
|
## 🔧 Opções de CLI
|
|
84
90
|
|
|
85
|
-
| Opção
|
|
86
|
-
|
|
87
|
-
| `--port=<number>`
|
|
88
|
-
| `--root=<path>`
|
|
89
|
-
| `--open=<true/false>`
|
|
90
|
-
| `--spa=<true/false>`
|
|
91
|
-
| `--https=<true/false>` | Habilitar modo HTTPS
|
|
91
|
+
| Opção | Descrição | Padrão |
|
|
92
|
+
| ---------------------- | ------------------------------- | --------------------- |
|
|
93
|
+
| `--port=<number>` | Porta do servidor | `9999` |
|
|
94
|
+
| `--root=<path>` | Diretório raiz | `.` (diretório atual) |
|
|
95
|
+
| `--open=<true/false>` | Abrir navegador automaticamente | `true` |
|
|
96
|
+
| `--spa=<true/false>` | Habilitar suporte SPA | `false` |
|
|
97
|
+
| `--https=<true/false>` | Habilitar modo HTTPS | `false` |
|
|
92
98
|
|
|
93
99
|
## 🏗️ Como foi feito
|
|
94
100
|
|
|
95
101
|
Este projeto foi desenvolvido seguindo uma arquitetura minimalista e moderna:
|
|
96
102
|
|
|
97
103
|
### Técnicas Utilizadas
|
|
104
|
+
|
|
98
105
|
1. **TypeScript Estrito**: Tipagem forte em todo o código
|
|
99
106
|
2. **APIs Nativas**: Uso exclusivo de módulos `node:*`
|
|
100
107
|
3. **Server-Sent Events**: Comunicação bidirecional eficiente
|
|
@@ -103,6 +110,7 @@ Este projeto foi desenvolvido seguindo uma arquitetura minimalista e moderna:
|
|
|
103
110
|
6. **CSS Injection**: DOM manipulation para hot reload inteligente
|
|
104
111
|
|
|
105
112
|
### Arquitetura
|
|
113
|
+
|
|
106
114
|
```
|
|
107
115
|
src/
|
|
108
116
|
├── index.ts # CLI e configuração
|
|
@@ -112,6 +120,7 @@ src/
|
|
|
112
120
|
```
|
|
113
121
|
|
|
114
122
|
### Funcionamento
|
|
123
|
+
|
|
115
124
|
1. **Watcher** monitora mudanças recursivamente usando `fs.watch`
|
|
116
125
|
2. **Server** serve arquivos estáticos com MIME types corretos
|
|
117
126
|
3. **SSE** notifica clientes sobre mudanças em tempo real
|
|
@@ -121,30 +130,36 @@ src/
|
|
|
121
130
|
## 🧪 Como testar
|
|
122
131
|
|
|
123
132
|
### Teste CSS Injection
|
|
124
|
-
|
|
133
|
+
|
|
134
|
+
1. Inicie o servidor: `one-server-4-all`
|
|
125
135
|
2. Modifique qualquer arquivo `.css`
|
|
126
136
|
3. Observe que apenas o CSS é atualizado, sem reload da página
|
|
127
137
|
|
|
128
138
|
### Teste SPA Support
|
|
129
|
-
|
|
139
|
+
|
|
140
|
+
1. Inicie com SPA: `one-server-4-all --spa=true`
|
|
130
141
|
2. Acesse `/qualquer-rota-inexistente`
|
|
131
142
|
3. Deve carregar `index.html` em vez de 404
|
|
132
143
|
|
|
133
144
|
### Teste MIME Types
|
|
145
|
+
|
|
134
146
|
1. Adicione arquivos de vídeo/fonte no seu projeto
|
|
135
147
|
2. Eles serão servidos com headers corretos
|
|
136
148
|
|
|
137
149
|
### Teste CORS
|
|
150
|
+
|
|
138
151
|
1. Acesse arquivos de outro domínio/origin
|
|
139
152
|
2. Deve funcionar sem erros de CORS
|
|
140
153
|
|
|
141
154
|
### Teste HTTPS
|
|
142
|
-
|
|
155
|
+
|
|
156
|
+
1. Execute: `one-server-4-all --https=true`
|
|
143
157
|
2. Observe o emoji 🔒 no log do terminal
|
|
144
158
|
3. Acesse https://localhost:9999
|
|
145
159
|
4. Aceite o aviso de certificado auto-assinado
|
|
146
160
|
|
|
147
161
|
### Teste Logs Detalhados
|
|
162
|
+
|
|
148
163
|
1. Abra uma página HTML
|
|
149
164
|
2. Observe no terminal:
|
|
150
165
|
- 📄 Arquivos servidos com tamanho e tipo MIME
|
|
@@ -154,20 +169,21 @@ src/
|
|
|
154
169
|
## 📊 Comparação com Live Server
|
|
155
170
|
|
|
156
171
|
| Feature | Live Server | Purecore Hot Server |
|
|
157
|
-
|
|
158
|
-
| **Dependencies** | Múltiplas | ❌ Zero
|
|
159
|
-
| **Language** | JavaScript | ✅ TypeScript
|
|
160
|
-
| **CSS Injection** | ✅ Sim
|
|
161
|
-
| **SPA Support** | ✅ Sim
|
|
162
|
-
| **MIME Types** | Básicos | ✅ Robustos
|
|
163
|
-
| **CORS** | ❌ Não
|
|
164
|
-
| **Installation** | Lento | ✅ Instantâneo
|
|
172
|
+
| ----------------- | ----------- | ------------------- |
|
|
173
|
+
| **Dependencies** | Múltiplas | ❌ Zero |
|
|
174
|
+
| **Language** | JavaScript | ✅ TypeScript |
|
|
175
|
+
| **CSS Injection** | ✅ Sim | ✅ Sim |
|
|
176
|
+
| **SPA Support** | ✅ Sim | ✅ Sim |
|
|
177
|
+
| **MIME Types** | Básicos | ✅ Robustos |
|
|
178
|
+
| **CORS** | ❌ Não | ✅ Sim |
|
|
179
|
+
| **Installation** | Lento | ✅ Instantâneo |
|
|
165
180
|
|
|
166
181
|
## 🔍 Análise de Dependências Obsoletas
|
|
167
182
|
|
|
168
183
|
Durante o desenvolvimento, analisamos dependências comuns em servidores de desenvolvimento e identificamos quais são desnecessárias no Node.js moderno:
|
|
169
184
|
|
|
170
185
|
### ❌ Dependências Obsoletas
|
|
186
|
+
|
|
171
187
|
- **`object-assign`**: Substituído por `Object.assign()` nativo
|
|
172
188
|
- **`http-auth`**: Autenticação HTTP pode ser feita nativamente
|
|
173
189
|
- **`colors`**: Node.js 20+ tem `util.styleText()` nativo
|
|
@@ -175,6 +191,7 @@ Durante o desenvolvimento, analisamos dependências comuns em servidores de dese
|
|
|
175
191
|
- **`event-stream`**: ⚠️ Vulnerabilidade conhecida, usar streams nativos
|
|
176
192
|
|
|
177
193
|
### ⚡ Alternativas Nativas Utilizadas
|
|
194
|
+
|
|
178
195
|
- **File System**: `node:fs` com `fs.watch()` recursivo
|
|
179
196
|
- **HTTP Server**: `node:http` e `node:https` nativos
|
|
180
197
|
- **Crypto**: `node:crypto` para certificados HTTPS
|
|
@@ -182,6 +199,7 @@ Durante o desenvolvimento, analisamos dependências comuns em servidores de dese
|
|
|
182
199
|
- **Streams**: `node:stream` para Server-Sent Events
|
|
183
200
|
|
|
184
201
|
### 🎯 Resultado
|
|
202
|
+
|
|
185
203
|
**Zero dependências externas** = instalação instantânea, sem vulnerabilidades de terceiros, e compatibilidade garantida com futuras versões do Node.js.
|
|
186
204
|
|
|
187
205
|
## 📝 Changelog
|
|
@@ -194,4 +212,4 @@ Contribuições são bem-vindas! Este projeto segue uma filosofia de **zero depe
|
|
|
194
212
|
|
|
195
213
|
## 📄 Licença
|
|
196
214
|
|
|
197
|
-
MIT
|
|
215
|
+
MIT
|
package/dist/cert-generator.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import crypto from
|
|
3
|
-
import fs from
|
|
4
|
-
import path from
|
|
5
|
-
import { execFileSync } from
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
6
|
export class CertGenerator {
|
|
7
|
-
static CERT_DIR =
|
|
8
|
-
static KEY_FILE =
|
|
9
|
-
static CERT_FILE =
|
|
7
|
+
static CERT_DIR = ".one-server-4-all-certs";
|
|
8
|
+
static KEY_FILE = "localhost.key";
|
|
9
|
+
static CERT_FILE = "localhost.crt";
|
|
10
10
|
static async generateCerts() {
|
|
11
11
|
const certDir = path.join(process.cwd(), this.CERT_DIR);
|
|
12
12
|
const keyPath = path.join(certDir, this.KEY_FILE);
|
|
@@ -16,7 +16,7 @@ export class CertGenerator {
|
|
|
16
16
|
try {
|
|
17
17
|
await fs.access(keyPath);
|
|
18
18
|
await fs.access(certPath);
|
|
19
|
-
console.log(
|
|
19
|
+
console.log("📋 Certificados já existem em:", certDir);
|
|
20
20
|
return { keyPath, certPath };
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
@@ -24,7 +24,7 @@ export class CertGenerator {
|
|
|
24
24
|
}
|
|
25
25
|
// Criar diretório se não existir
|
|
26
26
|
await fs.mkdir(certDir, { recursive: true });
|
|
27
|
-
console.log(
|
|
27
|
+
console.log("🔐 Gerando certificados auto-assinados...");
|
|
28
28
|
// Observação importante:
|
|
29
29
|
// Gerar um X509 "de verdade" apenas com node:crypto (sem libs externas) não é trivial.
|
|
30
30
|
// Como este projeto roda no WSL, utilizamos o OpenSSL (toolchain padrão do Linux)
|
|
@@ -32,44 +32,58 @@ export class CertGenerator {
|
|
|
32
32
|
await this.generateWithOpenSSL({ keyPath, certPath });
|
|
33
33
|
// Validação do PEM (evita subir servidor com arquivo corrompido)
|
|
34
34
|
const [keyPem, certPem] = await Promise.all([
|
|
35
|
-
fs.readFile(keyPath,
|
|
36
|
-
fs.readFile(certPath,
|
|
35
|
+
fs.readFile(keyPath, "utf8"),
|
|
36
|
+
fs.readFile(certPath, "utf8"),
|
|
37
37
|
]);
|
|
38
38
|
this.validatePem({ keyPem, certPem, keyPath, certPath });
|
|
39
39
|
this.validateX509(certPem, certPath);
|
|
40
40
|
this.validateWithOpenSSL(certPath);
|
|
41
|
-
console.log(
|
|
42
|
-
console.log(
|
|
43
|
-
console.log(
|
|
44
|
-
console.log(
|
|
45
|
-
console.log(
|
|
46
|
-
console.log(
|
|
47
|
-
console.log(
|
|
41
|
+
console.log("✅ Certificados gerados com sucesso!");
|
|
42
|
+
console.log("📁 Localização:", certDir);
|
|
43
|
+
console.log("🔑 Chave privada:", keyPath);
|
|
44
|
+
console.log("📄 Certificado:", certPath);
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log("⚠️ AVISO: Estes são certificados auto-assinados para desenvolvimento local.");
|
|
47
|
+
console.log(" Não use em produção!");
|
|
48
48
|
return { keyPath, certPath };
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
|
-
console.error(
|
|
51
|
+
console.error("❌ Erro ao gerar certificados:", error);
|
|
52
52
|
throw error;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
static generateWithOpenSSL(params) {
|
|
56
56
|
const { keyPath, certPath } = params;
|
|
57
|
-
const subj =
|
|
57
|
+
const subj = "/C=BR/ST=SP/L=Sao Paulo/O=Purecore/OU=Dev/CN=localhost";
|
|
58
58
|
// Preferimos SAN para evitar problemas em clients modernos.
|
|
59
59
|
// Nem todo OpenSSL antigo suporta -addext, então fazemos fallback.
|
|
60
|
-
const baseArgs = [
|
|
60
|
+
const baseArgs = [
|
|
61
|
+
"req",
|
|
62
|
+
"-x509",
|
|
63
|
+
"-newkey",
|
|
64
|
+
"rsa:2048",
|
|
65
|
+
"-keyout",
|
|
66
|
+
keyPath,
|
|
67
|
+
"-out",
|
|
68
|
+
certPath,
|
|
69
|
+
"-days",
|
|
70
|
+
"365",
|
|
71
|
+
"-nodes",
|
|
72
|
+
"-subj",
|
|
73
|
+
subj,
|
|
74
|
+
];
|
|
61
75
|
try {
|
|
62
|
-
execFileSync(
|
|
76
|
+
execFileSync("openssl", [...baseArgs, "-addext", "subjectAltName=DNS:localhost,IP:127.0.0.1"], { stdio: "inherit" });
|
|
63
77
|
}
|
|
64
78
|
catch {
|
|
65
|
-
execFileSync(
|
|
79
|
+
execFileSync("openssl", baseArgs, { stdio: "inherit" });
|
|
66
80
|
}
|
|
67
81
|
}
|
|
68
82
|
static validatePem(params) {
|
|
69
83
|
const { keyPem, certPem, keyPath, certPath } = params;
|
|
70
|
-
const keyOk = keyPem.includes(
|
|
71
|
-
keyPem.includes(
|
|
72
|
-
const certOk = certPem.includes(
|
|
84
|
+
const keyOk = keyPem.includes("-----BEGIN PRIVATE KEY-----") ||
|
|
85
|
+
keyPem.includes("-----BEGIN RSA PRIVATE KEY-----");
|
|
86
|
+
const certOk = certPem.includes("-----BEGIN CERTIFICATE-----");
|
|
73
87
|
if (!keyOk || !certOk) {
|
|
74
88
|
throw new Error(`PEM inválido gerado. ` +
|
|
75
89
|
`keyOk=${keyOk} certOk=${certOk}. ` +
|
|
@@ -83,7 +97,7 @@ export class CertGenerator {
|
|
|
83
97
|
// @ts-ignore - Bun/Node expõem X509Certificate em node:crypto
|
|
84
98
|
const x509 = new crypto.X509Certificate(certPem);
|
|
85
99
|
if (!x509.subject) {
|
|
86
|
-
throw new Error(
|
|
100
|
+
throw new Error("X509 sem subject");
|
|
87
101
|
}
|
|
88
102
|
}
|
|
89
103
|
catch (error) {
|
|
@@ -93,7 +107,9 @@ export class CertGenerator {
|
|
|
93
107
|
static validateWithOpenSSL(certPath) {
|
|
94
108
|
try {
|
|
95
109
|
// OpenSSL é a validação "ground truth" no WSL.
|
|
96
|
-
execFileSync(
|
|
110
|
+
execFileSync("openssl", ["x509", "-in", certPath, "-noout"], {
|
|
111
|
+
stdio: "ignore",
|
|
112
|
+
});
|
|
97
113
|
}
|
|
98
114
|
catch (error) {
|
|
99
115
|
throw new Error(`OpenSSL não conseguiu ler o certificado (${certPath}).`);
|
|
@@ -116,40 +132,42 @@ export class CertGenerator {
|
|
|
116
132
|
const certDir = path.join(process.cwd(), this.CERT_DIR);
|
|
117
133
|
try {
|
|
118
134
|
await fs.rm(certDir, { recursive: true, force: true });
|
|
119
|
-
console.log(
|
|
135
|
+
console.log("🗑️ Certificados removidos:", certDir);
|
|
120
136
|
}
|
|
121
137
|
catch (error) {
|
|
122
|
-
console.error(
|
|
138
|
+
console.error("❌ Erro ao remover certificados:", error);
|
|
123
139
|
}
|
|
124
140
|
}
|
|
125
141
|
}
|
|
126
142
|
// Executar se chamado diretamente
|
|
127
|
-
if (typeof require !==
|
|
143
|
+
if (typeof require !== "undefined" && require.main === module) {
|
|
128
144
|
const command = process.argv[2];
|
|
129
145
|
switch (command) {
|
|
130
|
-
case
|
|
146
|
+
case "generate":
|
|
131
147
|
case undefined:
|
|
132
148
|
CertGenerator.generateCerts().catch(console.error);
|
|
133
149
|
break;
|
|
134
|
-
case
|
|
150
|
+
case "clean":
|
|
135
151
|
CertGenerator.cleanCerts().catch(console.error);
|
|
136
152
|
break;
|
|
137
|
-
case
|
|
138
|
-
CertGenerator.getCertPaths()
|
|
153
|
+
case "info":
|
|
154
|
+
CertGenerator.getCertPaths()
|
|
155
|
+
.then((paths) => {
|
|
139
156
|
if (paths) {
|
|
140
|
-
console.log(
|
|
141
|
-
console.log(
|
|
142
|
-
console.log(
|
|
157
|
+
console.log("📋 Certificados encontrados:");
|
|
158
|
+
console.log("🔑 Chave:", paths.keyPath);
|
|
159
|
+
console.log("📄 Certificado:", paths.certPath);
|
|
143
160
|
}
|
|
144
161
|
else {
|
|
145
|
-
console.log(
|
|
162
|
+
console.log("❌ Nenhum certificado encontrado");
|
|
146
163
|
}
|
|
147
|
-
})
|
|
164
|
+
})
|
|
165
|
+
.catch(console.error);
|
|
148
166
|
break;
|
|
149
167
|
default:
|
|
150
|
-
console.log(
|
|
151
|
-
console.log(
|
|
152
|
-
console.log(
|
|
153
|
-
console.log(
|
|
168
|
+
console.log("Uso: cert-generator [generate|clean|info]");
|
|
169
|
+
console.log(" generate: Gera certificados auto-assinados (padrão)");
|
|
170
|
+
console.log(" clean: Remove certificados existentes");
|
|
171
|
+
console.log(" info: Mostra informações dos certificados");
|
|
154
172
|
}
|
|
155
173
|
}
|
package/dist/deployer.js
CHANGED
|
@@ -16,7 +16,7 @@ export class Deployer {
|
|
|
16
16
|
return new Promise((resolve) => this.rl.question(query, resolve));
|
|
17
17
|
}
|
|
18
18
|
printBanner() {
|
|
19
|
-
console.log(`\n ${bold(magenta("🚀
|
|
19
|
+
console.log(`\n ${bold(magenta("🚀 one-server-4-all DEPLOYER"))} ${gray("v0.4.0")}`);
|
|
20
20
|
console.log(` ${gray("─────────────────────────────────────────")}\n`);
|
|
21
21
|
}
|
|
22
22
|
async start() {
|
|
@@ -27,7 +27,7 @@ export class Deployer {
|
|
|
27
27
|
this.rl.close();
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
const port = (await this.question(` ${cyan("➜")} ${bold("Qual a porta do servidor?")} ${gray("(padrão
|
|
30
|
+
const port = (await this.question(` ${cyan("➜")} ${bold("Qual a porta do servidor?")} ${gray("(padrão 7000)")}\n ${green("❯")} `)) || "7000";
|
|
31
31
|
const confirmNginx = await this.question(` ${cyan("➜")} ${bold("Deseja configurar Nginx + SSL (Certbot) agora?")} ${gray("(s/n)")}\n ${green("❯")} `);
|
|
32
32
|
let certPaths = { key: "", cert: "" };
|
|
33
33
|
if (confirmNginx.toLowerCase() === "s") {
|
|
@@ -40,18 +40,17 @@ export class Deployer {
|
|
|
40
40
|
}
|
|
41
41
|
const name = domain.split(".")[0];
|
|
42
42
|
// Monta o comando PM2
|
|
43
|
-
//
|
|
44
|
-
// O
|
|
45
|
-
//
|
|
46
|
-
let pm2Command
|
|
43
|
+
// Quando Nginx+Certbot está configurado, o Nginx faz terminação SSL
|
|
44
|
+
// O server roda em HTTP simples (Nginx faz proxy_pass http://localhost:porta)
|
|
45
|
+
// Argumentos devem estar DENTRO das aspas para PM2 processar corretamente
|
|
46
|
+
let pm2Command;
|
|
47
47
|
if (certPaths.key && certPaths.cert) {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
pm2Command += ` --https=true --ssl-key="${certPaths.key}" --ssl-cert="${certPaths.cert}"`;
|
|
48
|
+
// Com Nginx+Certbot: server roda HTTP, Nginx cuida do SSL
|
|
49
|
+
pm2Command = `pm2 start "npx vai-server --port=${port} --open=false" --name "${domain}"`;
|
|
51
50
|
}
|
|
52
51
|
else {
|
|
53
|
-
//
|
|
54
|
-
pm2Command
|
|
52
|
+
// Sem Nginx: pode rodar HTTPS auto-assinado ou HTTP
|
|
53
|
+
pm2Command = `pm2 start "npx vai-server --port=${port} --open=false" --name "${domain}"`;
|
|
55
54
|
}
|
|
56
55
|
console.log(`\n ${bold("📦 Configuração Final:")}`);
|
|
57
56
|
console.log(` ${gray("────────────────────────")}`);
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ else {
|
|
|
21
21
|
// Se o primeiro argumento não tiver --, assumimos que é a pasta
|
|
22
22
|
const rootArg = args[0] && !args[0].startsWith("--") ? args[0] : ".";
|
|
23
23
|
const rawConfig = {
|
|
24
|
-
port: parseInt(getArg("port", "
|
|
24
|
+
port: parseInt(getArg("port", "7000")),
|
|
25
25
|
root: path.resolve(process.cwd(), rootArg),
|
|
26
26
|
open: getArg("open", "true"), // 'true' por padrão
|
|
27
27
|
spa: getArg("spa", "false"), // 'false' por padrão
|