e5ed1d3c9f
- Add tauri/ subfolder: a Tauri-based desktop shell that loads the same index.html as the Electron app, producing a ~2MB installer (vs ~71MB for Electron) by using the OS WebView2 instead of bundled Chromium. Includes sync-frontend.js to keep tauri/dist/ in sync with the canonical index.html and vendor/ at build time (no UI duplication/drift). - Vendor lucide, jsPDF, and jspdf-autotable locally under vendor/ instead of loading from CDNs, so the app works fully offline in both shells. - Fix server.js path traversal vulnerability (sanitize/contain request paths). - Fix exportPDF() using stale/edited piece data that could produce Infinity/NaN in generated PDFs; add a recalculate guard. - Fix race conditions: overlapping copy/save-defaults button revert timers, and FileReader callbacks for logo/CSV import that could apply out of order. - Minor cleanups: explicit array index parsing, hasResult() helper, consistent event binding for NMFC search results, cached DOM lookups. - Document the dual-shell architecture and vendoring conventions in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
1.4 KiB
JavaScript
44 lines
1.4 KiB
JavaScript
const http = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const PORT = 3010;
|
|
|
|
const mimeTypes = {
|
|
'.html': 'text/html',
|
|
'.js': 'application/javascript',
|
|
'.css': 'text/css',
|
|
'.png': 'image/png',
|
|
'.ico': 'image/x-icon',
|
|
'.json': 'application/json',
|
|
};
|
|
|
|
const server = http.createServer((req, res) => {
|
|
// Strip query string / hash and decode URI components before resolving,
|
|
// then confirm the resolved path stays inside __dirname (no ../ escapes).
|
|
const urlPath = decodeURIComponent(req.url.split('?')[0].split('#')[0]);
|
|
const reqPath = urlPath === '/' ? 'index.html' : urlPath.replace(/^\/+/, '');
|
|
const filePath = path.join(__dirname, reqPath);
|
|
if (!filePath.startsWith(__dirname + path.sep) && filePath !== __dirname) {
|
|
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
res.end('403 Forbidden');
|
|
return;
|
|
}
|
|
const ext = path.extname(filePath);
|
|
const contentType = mimeTypes[ext] || 'application/octet-stream';
|
|
|
|
fs.readFile(filePath, (err, data) => {
|
|
if (err) {
|
|
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
res.end('404 Not Found');
|
|
return;
|
|
}
|
|
res.writeHead(200, { 'Content-Type': contentType });
|
|
res.end(data);
|
|
});
|
|
});
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`Freight Calculator Pro running at http://localhost:${PORT}`);
|
|
});
|