free-framework 4.6.0 → 4.6.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-framework",
3
- "version": "4.6.0",
3
+ "version": "4.6.2",
4
4
  "description": "Professional Node.js engine for the .free language. Blazing-fast SSR, Islands Architecture, and built-in ORM.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -3,13 +3,13 @@
3
3
  * Enterprise Maintenance Mode Guard.
4
4
  */
5
5
 
6
- module.exports = function maintenanceMiddleware(req, res) {
6
+ module.exports = function maintenanceMiddleware(req, res, next) {
7
7
  const isMaintenance = process.env.MAINTENANCE_MODE === 'true';
8
8
  const bypassToken = process.env.MAINTENANCE_BYPASS;
9
9
 
10
10
  // Allow bypassing with a specific query token or if disabled
11
11
  if (!isMaintenance || (bypassToken && req.query.bypass === bypassToken)) {
12
- return;
12
+ return next();
13
13
  }
14
14
 
15
15
  // Identify if it's an API request or HTML
@@ -3,7 +3,7 @@
3
3
  * Enterprise-grade Security Headers Middleware.
4
4
  */
5
5
 
6
- module.exports = function securityMiddleware(req, res) {
6
+ module.exports = function securityMiddleware(req, res, next) {
7
7
  // 1. Core Security Headers
8
8
  res.setHeader('X-Powered-By', 'Free-Ultra/Enterprise');
9
9
  res.setHeader('X-Content-Type-Options', 'nosniff');
@@ -25,4 +25,6 @@ module.exports = function securityMiddleware(req, res) {
25
25
 
26
26
  // 4. Permissions Policy (Hardware restrictions)
27
27
  res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), interest-cohort=()');
28
+
29
+ next();
28
30
  };
package/runtime/server.js CHANGED
@@ -16,24 +16,36 @@ const { LRUCache } = require('lru-cache');
16
16
  class FreeServer {
17
17
  constructor() {
18
18
  this.app = new HyperExpress.Server();
19
+ this.viewsPath = process.env.VIEWS_PATH || nodePath.join(process.cwd(), 'views');
19
20
  this.namedMiddlewares = {};
20
21
  this.errorViews = {};
21
22
  this.plugins = [];
22
- this.viewsPath = process.env.VIEWS_PATH || nodePath.join(process.cwd(), 'views');
23
+
24
+ // Global Request Logger & Error Handler Initialization
25
+ this.app.set_error_handler((req, res, error) => {
26
+ console.error(`[Free Engine] 🔥 Global Uncaught Error:`, error);
27
+ this.handleError(req, res, error);
28
+ });
29
+
30
+ this.app.use((req, res, next) => {
31
+ console.log(`[Free Engine] 📥 Request: ${req.method} ${req.url}`);
32
+ if (next) next();
33
+ });
23
34
 
24
35
  // Static Asset Serving Middleware
25
36
  const publicPath = nodePath.join(process.cwd(), 'public');
26
- this.app.use((req, res) => {
37
+ this.app.use((req, res, next) => {
27
38
  const lookupPath = req.path.startsWith('/') ? req.path.substring(1) : req.path;
28
- if (!lookupPath) return;
39
+ if (!lookupPath) return next ? next() : null;
29
40
 
30
41
  const fullPath = nodePath.join(publicPath, lookupPath);
31
42
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
32
- const content = fs.readFileSync(fullPath);
33
43
  const ext = nodePath.extname(fullPath);
34
- const mimes = { '.js': 'application/javascript', '.css': 'text/css', '.png': 'image/png' };
35
- return res.type(mimes[ext] || 'application/javascript').send(content);
44
+ const mimes = { '.js': 'application/javascript', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml' };
45
+ res.type(mimes[ext] || 'text/plain');
46
+ return res.send(fs.readFileSync(fullPath));
36
47
  }
48
+ if (next) next();
37
49
  });
38
50
 
39
51
  this.app.get('/test-static', (req, res) => res.send('Static serving test'));
@@ -54,17 +66,18 @@ class FreeServer {
54
66
  this.app.use(maintenanceMiddleware);
55
67
  this.app.use(securityMiddleware);
56
68
 
57
- this.app.use((req, res) => {
58
- const ip = req.ip || req.headers['x-forwarded-for'] || 'unknown';
69
+ this.app.use((req, res, next) => {
70
+ const ip = req.ip || req.headers['x-forwarded-for'] || '127.0.0.1';
59
71
  const reqCount = this.rateLimits.get(ip) || 0;
60
72
 
61
73
  if (reqCount >= 5000) {
62
74
  res.status(429);
63
75
  res.setHeader('Retry-After', '60');
64
- return res.send('Too Many Requests - Under DDoS Protection');
76
+ return res.send('Too Many Requests');
65
77
  }
66
78
 
67
79
  this.rateLimits.set(ip, reqCount + 1);
80
+ if (next) next();
68
81
  });
69
82
  }
70
83
 
@@ -96,58 +109,7 @@ class FreeServer {
96
109
 
97
110
  await handler(req, res);
98
111
  } catch (e) {
99
- if (process.env.NODE_ENV === 'production') {
100
- console.error(`[Free Engine] Error ${method} ${path}:`, e);
101
- res.status(e.status || 500).header('Content-Type', 'text/html').send('Internal Error');
102
- } else {
103
- // Premium Debug Page (Ignition Style)
104
- const os = require('os');
105
- const pkg = require('../package.json');
106
- const debugTplPath = nodePath.join(__dirname, 'views/debug.html');
107
-
108
- if (fs.existsSync(debugTplPath)) {
109
- let html = fs.readFileSync(debugTplPath, 'utf8');
110
-
111
- // Error Details
112
- html = html.replace('{{errorName}}', e.name || 'Error');
113
- html = html.replace('{{errorMessage}}', e.message || 'No message provided');
114
-
115
- // Stack Trace with highlighting
116
- const stack = (e.stack || '').split('\n').map(line => {
117
- const isUserFile = line.includes(process.cwd()) && !line.includes('node_modules');
118
- return `<span class="stack-line ${isUserFile ? 'highlight' : ''}">${line.replace(/&/g, '&amp;').replace(/</g, '&lt;')}</span>`;
119
- }).join('');
120
- html = html.replace('{{stackTrace}}', stack);
121
-
122
- // Request Details
123
- let reqHtml = `<tr><th>Method</th><td class="val-text">${req.method}</td></tr>`;
124
- reqHtml += `<tr><th>URL</th><td class="val-text">${req.url}</td></tr>`;
125
- Object.keys(req.headers).forEach(k => {
126
- reqHtml += `<tr><th>Header: ${k}</th><td class="val-text">${req.headers[k]}</td></tr>`;
127
- });
128
- html = html.replace('{{requestDetails}}', reqHtml);
129
-
130
- // Env details (Mask secrets)
131
- let envHtml = '';
132
- Object.keys(process.env).forEach(k => {
133
- const isSecret = k.includes('KEY') || k.includes('SECRET') || k.includes('PASS');
134
- const val = isSecret ? '********' : process.env[k];
135
- envHtml += `<tr><th>${k}</th><td class="val-text">${val}</td></tr>`;
136
- });
137
- html = html.replace('{{envDetails}}', envHtml);
138
-
139
- // Engine Stats
140
- html = html.replace('{{version}}', pkg.version);
141
- html = html.replace('{{nodeVersion}}', process.version);
142
- html = html.replace('{{osInfo}}', `${os.type()} ${os.release()} (${os.arch()})`);
143
- const mem = process.memoryUsage();
144
- html = html.replace('{{memoryUsage}}', `${Math.round(mem.heapUsed / 1024 / 1024)}MB / ${Math.round(mem.heapTotal / 1024 / 1024)}MB`);
145
-
146
- res.status(500).header('Content-Type', 'text/html').send(html);
147
- } else {
148
- res.status(500).send(`<h1>${e.name || 'Error'}</h1><pre>${e.stack}</pre>`);
149
- }
150
- }
112
+ this.handleError(req, res, e);
151
113
  }
152
114
  };
153
115
 
@@ -155,6 +117,54 @@ class FreeServer {
155
117
  if (routeMethod === 'get') this.app.head(path, ...mwFns, routeHandler);
156
118
  }
157
119
 
120
+ handleError(req, res, e) {
121
+ if (process.env.NODE_ENV === 'production') {
122
+ res.status(500).header('Content-Type', 'text/html').send('Internal Error');
123
+ } else {
124
+ // Premium Debug Page (Ignition Style) logic moved here
125
+ const os = require('os');
126
+ const pkg = require('../package.json');
127
+ const debugTplPath = nodePath.join(__dirname, 'views/debug.html');
128
+
129
+ if (fs.existsSync(debugTplPath)) {
130
+ let html = fs.readFileSync(debugTplPath, 'utf8');
131
+ html = html.replace('{{errorName}}', e.name || 'Error');
132
+ html = html.replace('{{errorMessage}}', e.message || 'No message provided');
133
+
134
+ const stack = (e.stack || '').split('\n').map(line => {
135
+ const isUserFile = line.includes(process.cwd()) && !line.includes('node_modules');
136
+ return `<span class="stack-line ${isUserFile ? 'highlight' : ''}">${line.replace(/&/g, '&amp;').replace(/</g, '&lt;')}</span>`;
137
+ }).join('');
138
+ html = html.replace('{{stackTrace}}', stack);
139
+
140
+ let reqHtml = `<tr><th>Method</th><td class="val-text">${req.method}</td></tr>`;
141
+ reqHtml += `<tr><th>URL</th><td class="val-text">${req.url}</td></tr>`;
142
+ Object.keys(req.headers).forEach(k => {
143
+ reqHtml += `<tr><th>Header: ${k}</th><td class="val-text">${req.headers[k]}</td></tr>`;
144
+ });
145
+ html = html.replace('{{requestDetails}}', reqHtml);
146
+
147
+ let envHtml = '';
148
+ Object.keys(process.env).forEach(k => {
149
+ const isSecret = k.includes('KEY') || k.includes('SECRET') || k.includes('PASS');
150
+ const val = isSecret ? '********' : process.env[k];
151
+ envHtml += `<tr><th>${k}</th><td class="val-text">${val}</td></tr>`;
152
+ });
153
+ html = html.replace('{{envDetails}}', envHtml);
154
+
155
+ html = html.replace('{{version}}', pkg.version);
156
+ html = html.replace('{{nodeVersion}}', process.version);
157
+ html = html.replace('{{osInfo}}', `${os.type()} ${os.release()} (${os.arch()})`);
158
+ const mem = process.memoryUsage();
159
+ html = html.replace('{{memoryUsage}}', `${Math.round(mem.heapUsed / 1024 / 1024)}MB / ${Math.round(mem.heapTotal / 1024 / 1024)}MB`);
160
+
161
+ res.status(500).header('Content-Type', 'text/html').send(html);
162
+ } else {
163
+ res.status(500).send(`<h1>${e.name || 'Error'}</h1><pre>${e.stack}</pre>`);
164
+ }
165
+ }
166
+ }
167
+
158
168
  middleware(fn) { this.app.use(fn); }
159
169
  registerMiddleware(name, fn) { this.namedMiddlewares[name] = fn; }
160
170
  use(plugin) { plugin.install(this); this.plugins.push(plugin); }