hightjs 0.2.53 → 0.3.0
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/dist/adapters/express.js +1 -1
- package/dist/api/console.d.ts +6 -4
- package/dist/api/console.js +27 -9
- package/dist/auth/components.js +16 -0
- package/dist/auth/core.js +16 -0
- package/dist/auth/index.js +16 -0
- package/dist/auth/jwt.js +16 -0
- package/dist/auth/providers/index.js +16 -0
- package/dist/auth/providers.js +16 -0
- package/dist/auth/react/index.js +16 -0
- package/dist/auth/react.js +16 -0
- package/dist/auth/routes.js +16 -0
- package/dist/auth/types.js +16 -0
- package/dist/bin/hightjs.js +68 -11
- package/dist/builder.js +16 -0
- package/dist/client/clientRouter.js +16 -0
- package/dist/client/entry.client.js +16 -0
- package/dist/client.js +16 -0
- package/dist/helpers.d.ts +7 -6
- package/dist/helpers.js +284 -198
- package/dist/hotReload.js +23 -7
- package/dist/index.js +49 -25
- package/dist/router.js +16 -0
- package/dist/types.d.ts +6 -0
- package/docs/cli.md +4 -5
- package/package.json +1 -1
- package/src/adapters/express.ts +2 -1
- package/src/api/console.ts +12 -10
- package/src/auth/routes.ts +1 -0
- package/src/bin/hightjs.js +61 -11
- package/src/helpers.ts +311 -216
- package/src/hotReload.ts +8 -8
- package/src/index.ts +38 -25
- package/src/types.ts +6 -0
package/src/helpers.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
7
7
|
* You may obtain a copy of the License at
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
10
|
*
|
|
11
11
|
* Unless required by applicable law or agreed to in writing, software
|
|
12
12
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
@@ -15,267 +15,199 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
import
|
|
20
|
-
import type { HightJSOptions } from './types';
|
|
18
|
+
// Imports Nativos do Node.js (movidos para o topo)
|
|
19
|
+
import http, {IncomingMessage, Server, ServerResponse} from 'http';
|
|
21
20
|
import os from 'os';
|
|
21
|
+
import {URLSearchParams} from 'url'; // API moderna, substitui 'querystring'
|
|
22
|
+
// Helpers para integração com diferentes frameworks
|
|
23
|
+
import hweb, {FrameworkAdapterFactory} from './index'; // Importando o tipo
|
|
24
|
+
import type {HightJSOptions} from './types';
|
|
22
25
|
import Console, {Colors} from "./api/console";
|
|
26
|
+
import https, { Server as HttpsServer } from 'https'; // <-- ADICIONAR
|
|
27
|
+
import fs from 'fs'; // <-- ADICIONAR
|
|
28
|
+
// --- Tipagem ---
|
|
23
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Interface para a instância principal do hweb, inferida pelo uso.
|
|
32
|
+
*/
|
|
33
|
+
interface HWebApp {
|
|
34
|
+
prepare: () => Promise<void>;
|
|
35
|
+
// O handler pode ter assinaturas diferentes dependendo do framework
|
|
36
|
+
getRequestHandler: () => (req: any, res: any, next?: any) => Promise<void> | void;
|
|
37
|
+
setupWebSocket: (server: Server | any) => void; // Aceita http.Server ou app (Express/Fastify)
|
|
38
|
+
executeInstrumentation: () => void;
|
|
39
|
+
}
|
|
24
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Estende a request nativa do Node para incluir o body parseado.
|
|
43
|
+
*/
|
|
44
|
+
interface HWebIncomingMessage extends IncomingMessage {
|
|
45
|
+
body?: object | string | null;
|
|
46
|
+
}
|
|
25
47
|
|
|
48
|
+
// --- Helpers ---
|
|
26
49
|
|
|
27
|
-
|
|
28
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Encontra o IP externo local (rede)
|
|
52
|
+
*/
|
|
53
|
+
function getLocalExternalIp(): string {
|
|
29
54
|
const interfaces = os.networkInterfaces();
|
|
30
55
|
for (const name of Object.keys(interfaces)) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
56
|
+
const ifaceList = interfaces[name];
|
|
57
|
+
if (ifaceList) {
|
|
58
|
+
for (const iface of ifaceList) {
|
|
59
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
60
|
+
return iface.address;
|
|
61
|
+
}
|
|
34
62
|
}
|
|
35
63
|
}
|
|
36
64
|
}
|
|
37
|
-
return 'localhost';
|
|
65
|
+
return 'localhost'; // Fallback
|
|
38
66
|
}
|
|
39
67
|
|
|
40
|
-
const sendBox = (options:HightJSOptions) => {
|
|
68
|
+
const sendBox = (options: HightJSOptions) => {
|
|
41
69
|
const isDev = options.dev ? "Rodando em modo de desenvolvimento" : null;
|
|
42
|
-
const messages = [
|
|
43
|
-
` ${Colors.FgMagenta}● ${Colors.Reset}Local: ${Colors.FgGreen}http://localhost:${options.port}${Colors.Reset}`,
|
|
44
|
-
` ${Colors.FgMagenta}● ${Colors.Reset}Rede: ${Colors.FgGreen}http://${getLocalExternalIp()}:${options.port}${Colors.Reset}`,
|
|
45
|
-
]
|
|
46
|
-
if(isDev) {
|
|
47
|
-
messages.push(` ${Colors.FgMagenta}● ${Colors.Reset}${isDev}`)
|
|
48
|
-
}
|
|
49
|
-
Console.box(messages.join("\n"), {title: "Acesse o HightJS em:"})
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export default app
|
|
54
|
-
export function app(options: HightJSOptions = {}) {
|
|
55
|
-
const framework = options.framework || 'native'; // Mudando o padrão para 'native'
|
|
56
|
-
FrameworkAdapterFactory.setFramework(framework)
|
|
57
|
-
|
|
58
|
-
const hwebApp = hweb(options);
|
|
59
|
-
return {
|
|
60
|
-
...hwebApp,
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
|
|
64
|
-
*/
|
|
65
|
-
integrate: async (serverApp: any) => {
|
|
66
|
-
await hwebApp.prepare();
|
|
67
|
-
const handler = hwebApp.getRequestHandler();
|
|
68
|
-
|
|
69
|
-
if (framework === 'express') {
|
|
70
|
-
// Express integration
|
|
71
|
-
serverApp.use(handler);
|
|
72
|
-
hwebApp.setupWebSocket(serverApp);
|
|
73
|
-
} else if (framework === 'fastify') {
|
|
74
|
-
// Fastify integration
|
|
75
|
-
await serverApp.register(async (fastify: any) => {
|
|
76
|
-
fastify.all('*', handler);
|
|
77
|
-
});
|
|
78
|
-
hwebApp.setupWebSocket(serverApp);
|
|
79
|
-
} else {
|
|
80
|
-
// Generic integration - assume Express-like
|
|
81
|
-
serverApp.use(handler);
|
|
82
|
-
hwebApp.setupWebSocket(serverApp);
|
|
83
|
-
}
|
|
84
70
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
// 1. Verifica se o SSL está ativado (baseado na mesma lógica do initNativeServer)
|
|
72
|
+
const isSSL = options.ssl && options.ssl.key && options.ssl.cert;
|
|
73
|
+
const protocol = isSSL ? 'https' : 'http';
|
|
74
|
+
const localIp = getLocalExternalIp(); // Assume que getLocalExternalIp() existe
|
|
88
75
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
console.log(`${Colors.FgMagenta}
|
|
94
|
-
_ _ _ _ _ _ _____
|
|
95
|
-
| | | (_) | | | | | |/ ____|
|
|
96
|
-
| |__| |_ __ _| |__ | |_ | | (___
|
|
97
|
-
| __ | |/ _\` | '_ \\| __| _ | |\\___ \\
|
|
98
|
-
| | | | | (_| | | | | |_ | |__| |____) |
|
|
99
|
-
|_| |_|_|\\__, |_| |_|\\__| \\____/|_____/
|
|
100
|
-
__/ |
|
|
101
|
-
|___/ ${Colors.Reset}`)
|
|
102
|
-
const actualPort = options.port || 3000;
|
|
103
|
-
const actualHostname = options.hostname || "0.0.0.0";
|
|
76
|
+
// 2. Monta as mensagens com o protocolo correto
|
|
77
|
+
const messages = [
|
|
78
|
+
` ${Colors.FgGray}┃${Colors.Reset} Local: ${Colors.FgGreen}${protocol}://localhost:${options.port}${Colors.Reset}`,
|
|
79
|
+
];
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return await initFastifyServer(hwebApp, options, actualPort, actualHostname);
|
|
109
|
-
} else {
|
|
110
|
-
// Default to Native
|
|
111
|
-
return await initNativeServer(hwebApp, options, actualPort, actualHostname);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
81
|
+
// Só adiciona a rede se o IP local for encontrado
|
|
82
|
+
if (localIp) {
|
|
83
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} Rede: ${Colors.FgGreen}${protocol}://${localIp}:${options.port}${Colors.Reset}`);
|
|
114
84
|
}
|
|
115
|
-
}
|
|
116
85
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
*/
|
|
120
|
-
async function initExpressServer(hwebApp: any, options: HightJSOptions, port: number, hostname: string) {
|
|
121
|
-
const msg = Console.dynamicLine(` ${Colors.FgCyan}● ${Colors.Reset}Iniciando HightJS com Express...`);
|
|
122
|
-
const express = require('express');
|
|
123
|
-
const app = express();
|
|
124
|
-
|
|
125
|
-
// Middlewares básicos para Express
|
|
126
|
-
app.use(express.json());
|
|
127
|
-
app.use(express.urlencoded({ extended: true }));
|
|
128
|
-
|
|
129
|
-
// Cookie parser se disponível
|
|
130
|
-
try {
|
|
131
|
-
const cookieParser = require('cookie-parser');
|
|
132
|
-
app.use(cookieParser());
|
|
133
|
-
} catch (e) {
|
|
134
|
-
Console.error("Não foi possivel achar cookie-parser")
|
|
86
|
+
if (isDev) {
|
|
87
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} ${isDev}`);
|
|
135
88
|
}
|
|
136
89
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const server = app.listen(port, hostname, () => {
|
|
143
|
-
sendBox({ ...options, port });
|
|
144
|
-
msg.end(` ${Colors.FgCyan}● ${Colors.Reset}Servidor Express iniciado (compatibilidade)`);
|
|
145
|
-
});
|
|
90
|
+
// Adiciona aviso de redirecionamento se estiver em modo SSL
|
|
91
|
+
if (isSSL && options.ssl?.redirectPort) {
|
|
92
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} HTTP (porta ${options.ssl?.redirectPort}) está redirecionando para HTTPS.`);
|
|
93
|
+
}
|
|
146
94
|
|
|
147
|
-
|
|
148
|
-
hwebApp.setupWebSocket(server);
|
|
149
|
-
hwebApp.executeInstrumentation();
|
|
150
|
-
return server;
|
|
95
|
+
Console.box(messages.join("\n"), { title: "Acesse em:" });
|
|
151
96
|
}
|
|
152
97
|
|
|
153
98
|
/**
|
|
154
|
-
*
|
|
99
|
+
* Middleware para parsing do body com proteções de segurança (versão melhorada).
|
|
100
|
+
* Rejeita a promise em caso de erro de parsing ou estouro de limite.
|
|
155
101
|
*/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
await fastify.register(require('@fastify/formbody'));
|
|
169
|
-
} catch (e) {
|
|
170
|
-
Console.error("Não foi possivel achar @fastify/formbody")
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
await hwebApp.prepare();
|
|
174
|
-
const handler = hwebApp.getRequestHandler();
|
|
102
|
+
const parseBody = (req: IncomingMessage): Promise<object | string | null> => {
|
|
103
|
+
// Constantes para limites de segurança
|
|
104
|
+
const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB limite total
|
|
105
|
+
const MAX_JSON_SIZE = 1 * 1024 * 1024; // 1MB limite para JSON
|
|
106
|
+
const BODY_TIMEOUT = 30000; // 30 segundos
|
|
107
|
+
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
110
|
+
resolve(null);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
175
113
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
fastify.all('*', handler);
|
|
179
|
-
});
|
|
180
|
-
hwebApp.setupWebSocket(fastify);
|
|
114
|
+
let body = '';
|
|
115
|
+
let totalSize = 0;
|
|
181
116
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
117
|
+
// Timeout para requisições lentas
|
|
118
|
+
const timeout = setTimeout(() => {
|
|
119
|
+
req.destroy();
|
|
120
|
+
reject(new Error('Request body timeout'));
|
|
121
|
+
}, BODY_TIMEOUT);
|
|
188
122
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
*/
|
|
192
|
-
async function initNativeServer(hwebApp: any, options: HightJSOptions, port: number, hostname: string) {
|
|
193
|
-
const msg = Console.dynamicLine(` ${Colors.FgMagenta}⚡ ${Colors.Reset}${Colors.Bright}Iniciando HightJS em modo NATIVO${Colors.Reset}`);
|
|
123
|
+
req.on('data', (chunk: Buffer) => {
|
|
124
|
+
totalSize += chunk.length;
|
|
194
125
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
126
|
+
// Proteção contra DoS (Payload Too Large)
|
|
127
|
+
if (totalSize > MAX_BODY_SIZE) {
|
|
128
|
+
clearTimeout(timeout);
|
|
129
|
+
req.destroy();
|
|
130
|
+
reject(new Error('Request body too large'));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
body += chunk.toString();
|
|
134
|
+
});
|
|
198
135
|
|
|
199
|
-
|
|
200
|
-
|
|
136
|
+
req.on('end', () => {
|
|
137
|
+
clearTimeout(timeout);
|
|
201
138
|
|
|
202
|
-
|
|
203
|
-
const parseBody = (req: any): Promise<any> => {
|
|
204
|
-
return new Promise((resolve, reject) => {
|
|
205
|
-
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
139
|
+
if (!body) {
|
|
206
140
|
resolve(null);
|
|
207
141
|
return;
|
|
208
142
|
}
|
|
209
143
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const maxBodySize = 10 * 1024 * 1024; // 10MB limite
|
|
213
|
-
|
|
214
|
-
// Timeout para requisições que demoram muito
|
|
215
|
-
const timeout = setTimeout(() => {
|
|
216
|
-
req.destroy();
|
|
217
|
-
reject(new Error('Request timeout'));
|
|
218
|
-
}, 30000); // 30 segundos
|
|
219
|
-
|
|
220
|
-
req.on('data', (chunk: Buffer) => {
|
|
221
|
-
totalSize += chunk.length;
|
|
222
|
-
|
|
223
|
-
// Proteção contra ataques de DoS por body muito grande
|
|
224
|
-
if (totalSize > maxBodySize) {
|
|
225
|
-
clearTimeout(timeout);
|
|
226
|
-
req.destroy();
|
|
227
|
-
reject(new Error('Request body too large'));
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
body += chunk.toString();
|
|
232
|
-
});
|
|
144
|
+
try {
|
|
145
|
+
const contentType = req.headers['content-type'] || '';
|
|
233
146
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if (body.length > 1024 * 1024) { // 1MB limite para JSON
|
|
242
|
-
reject(new Error('JSON body too large'));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
147
|
+
if (contentType.includes('application/json')) {
|
|
148
|
+
if (body.length > MAX_JSON_SIZE) {
|
|
149
|
+
reject(new Error('JSON body too large'));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Rejeita promise se o JSON for inválido
|
|
153
|
+
try {
|
|
245
154
|
resolve(JSON.parse(body));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
} else {
|
|
249
|
-
resolve(body);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
reject(new Error('Invalid JSON body'));
|
|
250
157
|
}
|
|
251
|
-
}
|
|
252
|
-
|
|
158
|
+
} else if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
159
|
+
// Usa API moderna URLSearchParams (segura contra prototype pollution)
|
|
160
|
+
resolve(Object.fromEntries(new URLSearchParams(body)));
|
|
161
|
+
} else {
|
|
162
|
+
resolve(body); // Fallback para texto plano
|
|
253
163
|
}
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
req.on('error', (error: Error) => {
|
|
257
|
-
clearTimeout(timeout);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// Pega qualquer outro erro síncrono
|
|
258
166
|
reject(error);
|
|
259
|
-
}
|
|
167
|
+
}
|
|
260
168
|
});
|
|
261
|
-
};
|
|
262
169
|
|
|
263
|
-
|
|
264
|
-
|
|
170
|
+
req.on('error', (error: Error) => {
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
reject(error);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Inicializa servidor nativo do HightJS usando HTTP ou HTTPS
|
|
179
|
+
*/
|
|
180
|
+
async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port: number, hostname: string) {
|
|
181
|
+
const time = Date.now();
|
|
182
|
+
|
|
183
|
+
await hwebApp.prepare();
|
|
184
|
+
const handler = hwebApp.getRequestHandler();
|
|
185
|
+
const msg = Console.dynamicLine(` ${Colors.BgYellow} ready ${Colors.Reset} ${Colors.Bright}Iniciando HightJS na porta ${options.port}${Colors.Reset}`);
|
|
186
|
+
|
|
187
|
+
// --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
|
|
188
|
+
// Extraímos a lógica principal para uma variável
|
|
189
|
+
// para que possa ser usada tanto pelo servidor HTTP quanto HTTPS.
|
|
190
|
+
const requestListener = async (req: HWebIncomingMessage, res: ServerResponse) => {
|
|
265
191
|
// Configurações de segurança básicas
|
|
266
192
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
267
193
|
res.setHeader('X-Frame-Options', 'DENY');
|
|
268
194
|
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
269
195
|
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
270
196
|
|
|
271
|
-
//
|
|
197
|
+
// IMPORTANTE: Adiciona HSTS (Strict-Transport-Security) se estiver em modo SSL
|
|
198
|
+
// Isso força o navegador a usar HTTPS no futuro.
|
|
199
|
+
if (options.ssl) {
|
|
200
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Timeout por requisição
|
|
272
204
|
req.setTimeout(30000, () => {
|
|
273
205
|
res.statusCode = 408; // Request Timeout
|
|
274
206
|
res.end('Request timeout');
|
|
275
207
|
});
|
|
276
208
|
|
|
277
209
|
try {
|
|
278
|
-
// Validação básica de URL
|
|
210
|
+
// Validação básica de URL
|
|
279
211
|
const url = req.url || '/';
|
|
280
212
|
if (url.length > 2048) {
|
|
281
213
|
res.statusCode = 414; // URI Too Long
|
|
@@ -284,20 +216,25 @@ async function initNativeServer(hwebApp: any, options: HightJSOptions, port: num
|
|
|
284
216
|
}
|
|
285
217
|
|
|
286
218
|
// Parse do body com proteções
|
|
287
|
-
req.body = await parseBody(req);
|
|
219
|
+
req.body = await parseBody(req); // Assumindo que parseBody existe
|
|
288
220
|
|
|
289
|
-
// Adiciona host se não existir
|
|
221
|
+
// Adiciona host se não existir (necessário para `new URL`)
|
|
290
222
|
req.headers.host = req.headers.host || `localhost:${port}`;
|
|
291
223
|
|
|
292
224
|
// Chama o handler do HightJS
|
|
293
225
|
await handler(req, res);
|
|
226
|
+
|
|
294
227
|
} catch (error) {
|
|
295
|
-
|
|
228
|
+
// Log do erro no servidor
|
|
229
|
+
if (error instanceof Error) {
|
|
230
|
+
Console.error(`Erro no servidor nativo: ${error.message}`);
|
|
231
|
+
} else {
|
|
232
|
+
Console.error('Erro desconhecido no servidor nativo:', error);
|
|
233
|
+
}
|
|
296
234
|
|
|
235
|
+
// Tratamento de erro (idêntico ao seu original)
|
|
297
236
|
if (!res.headersSent) {
|
|
298
|
-
res.statusCode = 500;
|
|
299
237
|
res.setHeader('Content-Type', 'text/plain');
|
|
300
|
-
|
|
301
238
|
if (error instanceof Error) {
|
|
302
239
|
if (error.message.includes('too large')) {
|
|
303
240
|
res.statusCode = 413; // Payload Too Large
|
|
@@ -305,17 +242,62 @@ async function initNativeServer(hwebApp: any, options: HightJSOptions, port: num
|
|
|
305
242
|
} else if (error.message.includes('timeout')) {
|
|
306
243
|
res.statusCode = 408; // Request Timeout
|
|
307
244
|
res.end('Request timeout');
|
|
245
|
+
} else if (error.message.includes('Invalid JSON')) {
|
|
246
|
+
res.statusCode = 400; // Bad Request
|
|
247
|
+
res.end('Invalid JSON body');
|
|
308
248
|
} else {
|
|
249
|
+
res.statusCode = 500;
|
|
309
250
|
res.end('Internal server error');
|
|
310
251
|
}
|
|
311
252
|
} else {
|
|
253
|
+
res.statusCode = 500;
|
|
312
254
|
res.end('Internal server error');
|
|
313
255
|
}
|
|
314
256
|
}
|
|
315
257
|
}
|
|
316
|
-
}
|
|
258
|
+
};
|
|
259
|
+
// --- FIM DO LISTENER ---
|
|
260
|
+
|
|
261
|
+
let server: Server | HttpsServer; // O tipo do servidor pode variar
|
|
262
|
+
const isSSL = options.ssl && options.ssl.key && options.ssl.cert;
|
|
263
|
+
|
|
264
|
+
if (isSSL && options.ssl) {
|
|
265
|
+
|
|
266
|
+
const sslOptions = {
|
|
267
|
+
key: fs.readFileSync(options.ssl.key),
|
|
268
|
+
cert: fs.readFileSync(options.ssl.cert),
|
|
269
|
+
ca: options.ssl.ca ? fs.readFileSync(options.ssl.ca) : undefined
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// 1. Cria o servidor HTTPS principal
|
|
273
|
+
server = https.createServer(sslOptions, requestListener as any); // (any para contornar HWebIncomingMessage)
|
|
274
|
+
|
|
275
|
+
// 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
|
|
276
|
+
const httpRedirectPort = options.ssl.redirectPort;
|
|
277
|
+
http.createServer((req, res) => {
|
|
278
|
+
const host = req.headers['host'] || hostname;
|
|
279
|
+
// Remove a porta do host (ex: meusite.com:80)
|
|
280
|
+
const hostWithoutPort = host.split(':')[0];
|
|
281
|
+
|
|
282
|
+
// Monta a URL de redirecionamento
|
|
283
|
+
let redirectUrl = `https://${hostWithoutPort}`;
|
|
284
|
+
// Adiciona a porta HTTPS apenas se não for a padrão (443)
|
|
285
|
+
if (port !== 443) {
|
|
286
|
+
redirectUrl += `:${port}`;
|
|
287
|
+
}
|
|
288
|
+
redirectUrl += req.url || '/';
|
|
289
|
+
|
|
290
|
+
res.writeHead(301, { 'Location': redirectUrl });
|
|
291
|
+
res.end();
|
|
292
|
+
}).listen(httpRedirectPort, hostname, () => {});
|
|
317
293
|
|
|
318
|
-
|
|
294
|
+
} else {
|
|
295
|
+
// --- MODO HTTP (Original) ---
|
|
296
|
+
// Cria o servidor HTTP nativo
|
|
297
|
+
server = http.createServer(requestListener as any); // (any para contornar HWebIncomingMessage)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Configurações de segurança do servidor (Comum a ambos)
|
|
319
301
|
server.setTimeout(35000); // Timeout geral do servidor
|
|
320
302
|
server.maxHeadersCount = 100; // Limita número de headers
|
|
321
303
|
server.headersTimeout = 60000; // Timeout para headers
|
|
@@ -323,11 +305,124 @@ async function initNativeServer(hwebApp: any, options: HightJSOptions, port: num
|
|
|
323
305
|
|
|
324
306
|
server.listen(port, hostname, () => {
|
|
325
307
|
sendBox({ ...options, port });
|
|
326
|
-
msg.end(` ${Colors.
|
|
308
|
+
msg.end(` ${Colors.BgGreen} ready ${Colors.Reset} ${Colors.Bright}Pronto na porta ${Colors.BgGreen} ${options.port} ${Colors.Reset}${Colors.Bright} em ${Date.now() - time}ms${Colors.Reset}\n`);
|
|
327
309
|
});
|
|
328
310
|
|
|
329
|
-
// Configura WebSocket para hot reload
|
|
311
|
+
// Configura WebSocket para hot reload (Comum a ambos)
|
|
330
312
|
hwebApp.setupWebSocket(server);
|
|
331
313
|
hwebApp.executeInstrumentation();
|
|
332
314
|
return server;
|
|
333
315
|
}
|
|
316
|
+
|
|
317
|
+
// --- Função Principal ---
|
|
318
|
+
|
|
319
|
+
export function app(options: HightJSOptions = {}) {
|
|
320
|
+
const framework = options.framework || 'native';
|
|
321
|
+
FrameworkAdapterFactory.setFramework(framework)
|
|
322
|
+
|
|
323
|
+
// Tipando a app principal do hweb
|
|
324
|
+
const hwebApp: HWebApp = hweb(options);
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
...hwebApp,
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
|
|
331
|
+
* O 'serverApp: any' é mantido para flexibilidade, já que pode ser de tipos diferentes.
|
|
332
|
+
*/
|
|
333
|
+
integrate: async (serverApp: any) => {
|
|
334
|
+
await hwebApp.prepare();
|
|
335
|
+
const handler = hwebApp.getRequestHandler();
|
|
336
|
+
|
|
337
|
+
// O framework é setado nas opções do hweb, que deve
|
|
338
|
+
// retornar o handler correto em getRequestHandler()
|
|
339
|
+
// A lógica de integração original parece correta.
|
|
340
|
+
|
|
341
|
+
if (framework === 'express') {
|
|
342
|
+
const express = require('express');
|
|
343
|
+
try {
|
|
344
|
+
const cookieParser = require('cookie-parser');
|
|
345
|
+
serverApp.use(cookieParser());
|
|
346
|
+
} catch (e) {
|
|
347
|
+
Console.error("Não foi possivel achar cookie-parser");
|
|
348
|
+
}
|
|
349
|
+
serverApp.use(express.json());
|
|
350
|
+
serverApp.use(express.urlencoded({ extended: true }));
|
|
351
|
+
serverApp.use(handler);
|
|
352
|
+
hwebApp.setupWebSocket(serverApp);
|
|
353
|
+
|
|
354
|
+
} else if (framework === 'fastify') {
|
|
355
|
+
try {
|
|
356
|
+
await serverApp.register(require('@fastify/cookie'));
|
|
357
|
+
} catch (e) {
|
|
358
|
+
Console.error("Não foi possivel achar @fastify/cookie");
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
await serverApp.register(require('@fastify/formbody'));
|
|
362
|
+
} catch (e) {
|
|
363
|
+
Console.error("Não foi possivel achar @fastify/formbody");
|
|
364
|
+
}
|
|
365
|
+
await serverApp.register(async (fastify: any) => {
|
|
366
|
+
fastify.all('*', handler);
|
|
367
|
+
});
|
|
368
|
+
hwebApp.setupWebSocket(serverApp);
|
|
369
|
+
|
|
370
|
+
} else {
|
|
371
|
+
// Generic integration (assume Express-like)
|
|
372
|
+
serverApp.use(handler);
|
|
373
|
+
hwebApp.setupWebSocket(serverApp);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
hwebApp.executeInstrumentation();
|
|
377
|
+
return serverApp;
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Inicia um servidor HightJS fechado (o usuário não tem acesso ao framework)
|
|
382
|
+
*/
|
|
383
|
+
init: async () => {
|
|
384
|
+
const currentVersion = require('../package.json').version;
|
|
385
|
+
|
|
386
|
+
async function verifyVersion(): Promise<string> {
|
|
387
|
+
// node fetch
|
|
388
|
+
try {
|
|
389
|
+
const response = await fetch('https://registry.npmjs.org/hightjs/latest');
|
|
390
|
+
const data = await response.json();
|
|
391
|
+
return data.version;
|
|
392
|
+
} catch (error) {
|
|
393
|
+
Console.error('Não foi possível verificar a versão mais recente do HightJS:', error);
|
|
394
|
+
return currentVersion; // Retorna a versão atual em caso de erro
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const latestVersion = await verifyVersion();
|
|
398
|
+
const isUpToDate = latestVersion === currentVersion;
|
|
399
|
+
let message;
|
|
400
|
+
if (!isUpToDate) {
|
|
401
|
+
message = `${Colors.FgGreen} Há uma nova versão disponível (v${latestVersion})${Colors.FgMagenta}`
|
|
402
|
+
} else {
|
|
403
|
+
message = `${Colors.FgGreen} Você está na versão mais recente${Colors.FgMagenta}`
|
|
404
|
+
}
|
|
405
|
+
console.log(`${Colors.FgMagenta}
|
|
406
|
+
__ ___ ${Colors.FgGreen} __ ${Colors.FgMagenta}
|
|
407
|
+
|__| | / _\` |__| | ${Colors.FgGreen} | /__\` ${Colors.FgMagenta} ${Colors.FgMagenta}HightJS ${Colors.FgGray}(v${require('../package.json').version}) - itsmuzin${Colors.FgMagenta}
|
|
408
|
+
| | | \\__> | | | ${Colors.FgGreen}\\__/ .__/ ${message}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
${Colors.Reset}`);
|
|
412
|
+
|
|
413
|
+
const actualPort = options.port || 3000;
|
|
414
|
+
const actualHostname = options.hostname || "0.0.0.0";
|
|
415
|
+
|
|
416
|
+
if (framework !== 'native') {
|
|
417
|
+
Console.warn(`O framework "${framework}" foi selecionado, mas o método init() só funciona com o framework "native". Iniciando servidor nativo...`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
return await initNativeServer(hwebApp, options, actualPort, actualHostname);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Exporta a função 'app' como nomeada e também como padrão
|
|
428
|
+
export default app;
|