hightjs 0.2.52 → 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 +18 -1
- package/src/adapters/factory.ts +16 -0
- package/src/adapters/fastify.ts +16 -0
- package/src/adapters/native.ts +16 -0
- package/src/api/console.ts +28 -10
- package/src/api/http.ts +16 -0
- package/src/auth/client.ts +16 -0
- package/src/auth/components.tsx +16 -0
- package/src/auth/core.ts +16 -0
- package/src/auth/index.ts +16 -0
- package/src/auth/jwt.ts +16 -0
- package/src/auth/providers/credentials.ts +16 -0
- package/src/auth/providers/discord.ts +16 -0
- package/src/auth/providers/google.ts +16 -0
- package/src/auth/providers/index.ts +16 -0
- package/src/auth/providers.ts +16 -0
- package/src/auth/react/index.ts +16 -0
- package/src/auth/react.tsx +16 -0
- package/src/auth/routes.ts +17 -0
- package/src/auth/types.ts +17 -0
- package/src/bin/hightjs.js +79 -11
- package/src/builder.js +16 -0
- package/src/client/DefaultNotFound.tsx +16 -0
- package/src/client/clientRouter.ts +16 -0
- package/src/client/entry.client.tsx +16 -0
- package/src/client.ts +16 -0
- package/src/components/Link.tsx +16 -0
- package/src/helpers.ts +327 -215
- package/src/hotReload.ts +24 -8
- package/src/index.ts +54 -25
- package/src/renderer.tsx +16 -0
- package/src/router.ts +16 -0
- package/src/types/framework.ts +16 -0
- package/src/types.ts +22 -0
package/src/helpers.ts
CHANGED
|
@@ -1,264 +1,213 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the HightJS Project.
|
|
3
|
+
* Copyright (c) 2025 itsmuzin
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Imports Nativos do Node.js (movidos para o topo)
|
|
19
|
+
import http, {IncomingMessage, Server, ServerResponse} from 'http';
|
|
4
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';
|
|
5
25
|
import Console, {Colors} from "./api/console";
|
|
26
|
+
import https, { Server as HttpsServer } from 'https'; // <-- ADICIONAR
|
|
27
|
+
import fs from 'fs'; // <-- ADICIONAR
|
|
28
|
+
// --- Tipagem ---
|
|
6
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
|
+
}
|
|
7
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
|
+
}
|
|
8
47
|
|
|
48
|
+
// --- Helpers ---
|
|
9
49
|
|
|
10
|
-
|
|
11
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Encontra o IP externo local (rede)
|
|
52
|
+
*/
|
|
53
|
+
function getLocalExternalIp(): string {
|
|
12
54
|
const interfaces = os.networkInterfaces();
|
|
13
55
|
for (const name of Object.keys(interfaces)) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
+
}
|
|
17
62
|
}
|
|
18
63
|
}
|
|
19
64
|
}
|
|
20
|
-
return 'localhost';
|
|
65
|
+
return 'localhost'; // Fallback
|
|
21
66
|
}
|
|
22
67
|
|
|
23
|
-
const sendBox = (options:HightJSOptions) => {
|
|
68
|
+
const sendBox = (options: HightJSOptions) => {
|
|
24
69
|
const isDev = options.dev ? "Rodando em modo de desenvolvimento" : null;
|
|
25
|
-
const messages = [
|
|
26
|
-
` ${Colors.FgMagenta}● ${Colors.Reset}Local: ${Colors.FgGreen}http://localhost:${options.port}${Colors.Reset}`,
|
|
27
|
-
` ${Colors.FgMagenta}● ${Colors.Reset}Rede: ${Colors.FgGreen}http://${getLocalExternalIp()}:${options.port}${Colors.Reset}`,
|
|
28
|
-
]
|
|
29
|
-
if(isDev) {
|
|
30
|
-
messages.push(` ${Colors.FgMagenta}● ${Colors.Reset}${isDev}`)
|
|
31
|
-
}
|
|
32
|
-
Console.box(messages.join("\n"), {title: "Acesse o HightJS em:"})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export default app
|
|
37
|
-
export function app(options: HightJSOptions = {}) {
|
|
38
|
-
const framework = options.framework || 'native'; // Mudando o padrão para 'native'
|
|
39
|
-
FrameworkAdapterFactory.setFramework(framework)
|
|
40
70
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
44
75
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
await hwebApp.prepare();
|
|
50
|
-
const handler = hwebApp.getRequestHandler();
|
|
51
|
-
|
|
52
|
-
if (framework === 'express') {
|
|
53
|
-
// Express integration
|
|
54
|
-
serverApp.use(handler);
|
|
55
|
-
hwebApp.setupWebSocket(serverApp);
|
|
56
|
-
} else if (framework === 'fastify') {
|
|
57
|
-
// Fastify integration
|
|
58
|
-
await serverApp.register(async (fastify: any) => {
|
|
59
|
-
fastify.all('*', handler);
|
|
60
|
-
});
|
|
61
|
-
hwebApp.setupWebSocket(serverApp);
|
|
62
|
-
} else {
|
|
63
|
-
// Generic integration - assume Express-like
|
|
64
|
-
serverApp.use(handler);
|
|
65
|
-
hwebApp.setupWebSocket(serverApp);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
hwebApp.executeInstrumentation();
|
|
69
|
-
return serverApp;
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Inicia um servidor HightJS fechado (o usuário não tem acesso ao framework)
|
|
74
|
-
*/
|
|
75
|
-
init: async () => {
|
|
76
|
-
console.log(`${Colors.FgMagenta}
|
|
77
|
-
_ _ _ _ _ _ _____
|
|
78
|
-
| | | (_) | | | | | |/ ____|
|
|
79
|
-
| |__| |_ __ _| |__ | |_ | | (___
|
|
80
|
-
| __ | |/ _\` | '_ \\| __| _ | |\\___ \\
|
|
81
|
-
| | | | | (_| | | | | |_ | |__| |____) |
|
|
82
|
-
|_| |_|_|\\__, |_| |_|\\__| \\____/|_____/
|
|
83
|
-
__/ |
|
|
84
|
-
|___/ ${Colors.Reset}`)
|
|
85
|
-
const actualPort = options.port || 3000;
|
|
86
|
-
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
|
+
];
|
|
87
80
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return await initFastifyServer(hwebApp, options, actualPort, actualHostname);
|
|
92
|
-
} else {
|
|
93
|
-
// Default to Native
|
|
94
|
-
return await initNativeServer(hwebApp, options, actualPort, actualHostname);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
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}`);
|
|
97
84
|
}
|
|
98
|
-
}
|
|
99
85
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
*/
|
|
103
|
-
async function initExpressServer(hwebApp: any, options: HightJSOptions, port: number, hostname: string) {
|
|
104
|
-
const msg = Console.dynamicLine(` ${Colors.FgCyan}● ${Colors.Reset}Iniciando HightJS com Express...`);
|
|
105
|
-
const express = require('express');
|
|
106
|
-
const app = express();
|
|
107
|
-
|
|
108
|
-
// Middlewares básicos para Express
|
|
109
|
-
app.use(express.json());
|
|
110
|
-
app.use(express.urlencoded({ extended: true }));
|
|
111
|
-
|
|
112
|
-
// Cookie parser se disponível
|
|
113
|
-
try {
|
|
114
|
-
const cookieParser = require('cookie-parser');
|
|
115
|
-
app.use(cookieParser());
|
|
116
|
-
} catch (e) {
|
|
117
|
-
Console.error("Não foi possivel achar cookie-parser")
|
|
86
|
+
if (isDev) {
|
|
87
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} ${isDev}`);
|
|
118
88
|
}
|
|
119
89
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const server = app.listen(port, hostname, () => {
|
|
126
|
-
sendBox({ ...options, port });
|
|
127
|
-
msg.end(` ${Colors.FgCyan}● ${Colors.Reset}Servidor Express iniciado (compatibilidade)`);
|
|
128
|
-
});
|
|
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
|
+
}
|
|
129
94
|
|
|
130
|
-
|
|
131
|
-
hwebApp.setupWebSocket(server);
|
|
132
|
-
hwebApp.executeInstrumentation();
|
|
133
|
-
return server;
|
|
95
|
+
Console.box(messages.join("\n"), { title: "Acesse em:" });
|
|
134
96
|
}
|
|
135
97
|
|
|
136
98
|
/**
|
|
137
|
-
*
|
|
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.
|
|
138
101
|
*/
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
await fastify.register(require('@fastify/formbody'));
|
|
152
|
-
} catch (e) {
|
|
153
|
-
Console.error("Não foi possivel achar @fastify/formbody")
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await hwebApp.prepare();
|
|
157
|
-
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
|
+
}
|
|
158
113
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
fastify.all('*', handler);
|
|
162
|
-
});
|
|
163
|
-
hwebApp.setupWebSocket(fastify);
|
|
114
|
+
let body = '';
|
|
115
|
+
let totalSize = 0;
|
|
164
116
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
117
|
+
// Timeout para requisições lentas
|
|
118
|
+
const timeout = setTimeout(() => {
|
|
119
|
+
req.destroy();
|
|
120
|
+
reject(new Error('Request body timeout'));
|
|
121
|
+
}, BODY_TIMEOUT);
|
|
171
122
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
*/
|
|
175
|
-
async function initNativeServer(hwebApp: any, options: HightJSOptions, port: number, hostname: string) {
|
|
176
|
-
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;
|
|
177
125
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
+
});
|
|
181
135
|
|
|
182
|
-
|
|
183
|
-
|
|
136
|
+
req.on('end', () => {
|
|
137
|
+
clearTimeout(timeout);
|
|
184
138
|
|
|
185
|
-
|
|
186
|
-
const parseBody = (req: any): Promise<any> => {
|
|
187
|
-
return new Promise((resolve, reject) => {
|
|
188
|
-
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
139
|
+
if (!body) {
|
|
189
140
|
resolve(null);
|
|
190
141
|
return;
|
|
191
142
|
}
|
|
192
143
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const maxBodySize = 10 * 1024 * 1024; // 10MB limite
|
|
196
|
-
|
|
197
|
-
// Timeout para requisições que demoram muito
|
|
198
|
-
const timeout = setTimeout(() => {
|
|
199
|
-
req.destroy();
|
|
200
|
-
reject(new Error('Request timeout'));
|
|
201
|
-
}, 30000); // 30 segundos
|
|
202
|
-
|
|
203
|
-
req.on('data', (chunk: Buffer) => {
|
|
204
|
-
totalSize += chunk.length;
|
|
205
|
-
|
|
206
|
-
// Proteção contra ataques de DoS por body muito grande
|
|
207
|
-
if (totalSize > maxBodySize) {
|
|
208
|
-
clearTimeout(timeout);
|
|
209
|
-
req.destroy();
|
|
210
|
-
reject(new Error('Request body too large'));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
body += chunk.toString();
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
req.on('end', () => {
|
|
218
|
-
clearTimeout(timeout);
|
|
144
|
+
try {
|
|
145
|
+
const contentType = req.headers['content-type'] || '';
|
|
219
146
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
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 {
|
|
228
154
|
resolve(JSON.parse(body));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
} else {
|
|
232
|
-
resolve(body);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
reject(new Error('Invalid JSON body'));
|
|
233
157
|
}
|
|
234
|
-
}
|
|
235
|
-
|
|
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
|
|
236
163
|
}
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
req.on('error', (error: Error) => {
|
|
240
|
-
clearTimeout(timeout);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// Pega qualquer outro erro síncrono
|
|
241
166
|
reject(error);
|
|
242
|
-
}
|
|
167
|
+
}
|
|
243
168
|
});
|
|
244
|
-
};
|
|
245
169
|
|
|
246
|
-
|
|
247
|
-
|
|
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) => {
|
|
248
191
|
// Configurações de segurança básicas
|
|
249
192
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
250
193
|
res.setHeader('X-Frame-Options', 'DENY');
|
|
251
194
|
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
252
195
|
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
253
196
|
|
|
254
|
-
//
|
|
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
|
|
255
204
|
req.setTimeout(30000, () => {
|
|
256
205
|
res.statusCode = 408; // Request Timeout
|
|
257
206
|
res.end('Request timeout');
|
|
258
207
|
});
|
|
259
208
|
|
|
260
209
|
try {
|
|
261
|
-
// Validação básica de URL
|
|
210
|
+
// Validação básica de URL
|
|
262
211
|
const url = req.url || '/';
|
|
263
212
|
if (url.length > 2048) {
|
|
264
213
|
res.statusCode = 414; // URI Too Long
|
|
@@ -267,20 +216,25 @@ async function initNativeServer(hwebApp: any, options: HightJSOptions, port: num
|
|
|
267
216
|
}
|
|
268
217
|
|
|
269
218
|
// Parse do body com proteções
|
|
270
|
-
req.body = await parseBody(req);
|
|
219
|
+
req.body = await parseBody(req); // Assumindo que parseBody existe
|
|
271
220
|
|
|
272
|
-
// Adiciona host se não existir
|
|
221
|
+
// Adiciona host se não existir (necessário para `new URL`)
|
|
273
222
|
req.headers.host = req.headers.host || `localhost:${port}`;
|
|
274
223
|
|
|
275
224
|
// Chama o handler do HightJS
|
|
276
225
|
await handler(req, res);
|
|
226
|
+
|
|
277
227
|
} catch (error) {
|
|
278
|
-
|
|
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
|
+
}
|
|
279
234
|
|
|
235
|
+
// Tratamento de erro (idêntico ao seu original)
|
|
280
236
|
if (!res.headersSent) {
|
|
281
|
-
res.statusCode = 500;
|
|
282
237
|
res.setHeader('Content-Type', 'text/plain');
|
|
283
|
-
|
|
284
238
|
if (error instanceof Error) {
|
|
285
239
|
if (error.message.includes('too large')) {
|
|
286
240
|
res.statusCode = 413; // Payload Too Large
|
|
@@ -288,17 +242,62 @@ async function initNativeServer(hwebApp: any, options: HightJSOptions, port: num
|
|
|
288
242
|
} else if (error.message.includes('timeout')) {
|
|
289
243
|
res.statusCode = 408; // Request Timeout
|
|
290
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');
|
|
291
248
|
} else {
|
|
249
|
+
res.statusCode = 500;
|
|
292
250
|
res.end('Internal server error');
|
|
293
251
|
}
|
|
294
252
|
} else {
|
|
253
|
+
res.statusCode = 500;
|
|
295
254
|
res.end('Internal server error');
|
|
296
255
|
}
|
|
297
256
|
}
|
|
298
257
|
}
|
|
299
|
-
}
|
|
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, () => {});
|
|
293
|
+
|
|
294
|
+
} else {
|
|
295
|
+
// --- MODO HTTP (Original) ---
|
|
296
|
+
// Cria o servidor HTTP nativo
|
|
297
|
+
server = http.createServer(requestListener as any); // (any para contornar HWebIncomingMessage)
|
|
298
|
+
}
|
|
300
299
|
|
|
301
|
-
// Configurações de segurança do servidor
|
|
300
|
+
// Configurações de segurança do servidor (Comum a ambos)
|
|
302
301
|
server.setTimeout(35000); // Timeout geral do servidor
|
|
303
302
|
server.maxHeadersCount = 100; // Limita número de headers
|
|
304
303
|
server.headersTimeout = 60000; // Timeout para headers
|
|
@@ -306,11 +305,124 @@ async function initNativeServer(hwebApp: any, options: HightJSOptions, port: num
|
|
|
306
305
|
|
|
307
306
|
server.listen(port, hostname, () => {
|
|
308
307
|
sendBox({ ...options, port });
|
|
309
|
-
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`);
|
|
310
309
|
});
|
|
311
310
|
|
|
312
|
-
// Configura WebSocket para hot reload
|
|
311
|
+
// Configura WebSocket para hot reload (Comum a ambos)
|
|
313
312
|
hwebApp.setupWebSocket(server);
|
|
314
313
|
hwebApp.executeInstrumentation();
|
|
315
314
|
return server;
|
|
316
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;
|