Skip to content

Commit 3c76fa6

Browse files
committed
fix: comprehensive audit bug fixes and quality improvements
1 parent 5123281 commit 3c76fa6

9 files changed

Lines changed: 560 additions & 189 deletions

File tree

desktop-app/neutralino.config.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@
1313
"enabled": true,
1414
"writeToLogFile": true
1515
},
16-
"nativeAllowList": ["app.*", "os.*", "filesystem.*", "debug.log"],
16+
"nativeAllowList": [
17+
"app.exit",
18+
"os.showOpenDialog",
19+
"os.showSaveDialog",
20+
"os.showMessageBox",
21+
"os.open",
22+
"os.setTray",
23+
"filesystem.readFile",
24+
"filesystem.writeFile"
25+
],
1726
"globalVariables": {},
1827
"modes": {
1928
"window": {

desktop-app/prepare.js

Lines changed: 145 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,145 @@
1-
#!/usr/bin/env node
2-
3-
/**
4-
* prepare.js — Build script for the Neutralinojs desktop app.
5-
*
6-
* Copies shared browser-version files (script.js, styles.css, assets/)
7-
* from the repo root into desktop-app/resources/, and generates a
8-
* Neutralinojs-compatible index.html from the root index.html by
9-
* injecting the required Neutralinojs script tags and wrapper elements.
10-
*
11-
* Run from the desktop-app/ directory:
12-
* node prepare.js
13-
*/
14-
15-
const fs = require("fs");
16-
const path = require("path");
17-
18-
const ROOT_DIR = path.resolve(__dirname, "..");
19-
const RESOURCES_DIR = path.resolve(__dirname, "resources");
20-
21-
/** @section Copy shared files */
22-
23-
/**
24-
* Recursively copy a directory, creating target dirs as needed.
25-
*/
26-
function copyDirSync(src, dest) {
27-
fs.mkdirSync(dest, { recursive: true });
28-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
29-
const srcPath = path.join(src, entry.name);
30-
const destPath = path.join(dest, entry.name);
31-
if (entry.isDirectory()) {
32-
copyDirSync(srcPath, destPath);
33-
} else {
34-
fs.copyFileSync(srcPath, destPath);
35-
}
36-
}
37-
}
38-
39-
/** script.js → resources/js/script.js */
40-
const jsDest = path.join(RESOURCES_DIR, "js");
41-
fs.mkdirSync(jsDest, { recursive: true });
42-
fs.copyFileSync(
43-
path.join(ROOT_DIR, "script.js"),
44-
path.join(jsDest, "script.js"),
45-
);
46-
console.log("✓ Copied script.js → resources/js/script.js");
47-
48-
/** styles.css → resources/styles.css */
49-
fs.copyFileSync(
50-
path.join(ROOT_DIR, "styles.css"),
51-
path.join(RESOURCES_DIR, "styles.css"),
52-
);
53-
console.log("✓ Copied styles.css → resources/styles.css");
54-
55-
/** assets/ → resources/assets/ */
56-
copyDirSync(path.join(ROOT_DIR, "assets"), path.join(RESOURCES_DIR, "assets"));
57-
console.log("✓ Copied assets/ → resources/assets/");
58-
59-
/** @section Generate index.html with Neutralinojs injections */
60-
61-
let html = fs.readFileSync(path.join(ROOT_DIR, "index.html"), "utf-8");
62-
63-
/** Fix relative asset paths → absolute (Neutralinojs documentRoot is /resources/) */
64-
html = html.replace(/href="assets\//g, 'href="/assets/');
65-
html = html.replace(/href="styles\.css"/g, 'href="/styles.css"');
66-
/** Replace root script.js tag with neutralino.js + main.js + script.js under /js/ */
67-
html = html.replace(
68-
/<script\s+src="script\.js"\s*><\/script>/i,
69-
'<script src="/js/neutralino.js"></script>\n <script src="/js/main.js"></script>\n <script src="/js/script.js"></script>',
70-
);
71-
72-
/** Inject Neutralinojs app-info element after .app-container */
73-
html = html.replace(
74-
'<div class="app-container">',
75-
`<div class="app-container">
76-
<div id="neutralino-app">
77-
<div id="neutralino-info"></div>
78-
</div>`,
79-
);
80-
81-
fs.writeFileSync(path.join(RESOURCES_DIR, "index.html"), html, "utf-8");
82-
console.log(
83-
"✓ Generated resources/index.html (Neutralinojs injections applied)",
84-
);
85-
86-
console.log("\nDone! Run `npm run dev` to start the desktop app.");
1+
#!/usr/bin/env node
2+
3+
/**
4+
* prepare.js — Build script for the Neutralinojs desktop app.
5+
*
6+
* Copies shared browser-version files (script.js, styles.css, assets/)
7+
* from the repo root into desktop-app/resources/, downloads all remote CDN
8+
* libraries locally for 100% offline capabilities, and generates a
9+
* Neutralinojs-compatible index.html.
10+
*/
11+
12+
const fs = require("fs");
13+
const path = require("path");
14+
const https = require("https");
15+
16+
const ROOT_DIR = path.resolve(__dirname, "..");
17+
const RESOURCES_DIR = path.resolve(__dirname, "resources");
18+
const jsDest = path.join(RESOURCES_DIR, "js");
19+
const LIBS_DIR = path.join(RESOURCES_DIR, "libs");
20+
21+
// Create directories
22+
fs.mkdirSync(jsDest, { recursive: true });
23+
fs.mkdirSync(LIBS_DIR, { recursive: true });
24+
25+
function copyDirSync(src, dest) {
26+
fs.mkdirSync(dest, { recursive: true });
27+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
28+
const srcPath = path.join(src, entry.name);
29+
const destPath = path.join(dest, entry.name);
30+
if (entry.isDirectory()) {
31+
copyDirSync(srcPath, destPath);
32+
} else {
33+
fs.copyFileSync(srcPath, destPath);
34+
}
35+
}
36+
}
37+
38+
// Copy shared assets
39+
fs.copyFileSync(path.join(ROOT_DIR, "script.js"), path.join(jsDest, "script.js"));
40+
console.log("✓ Copied script.js → resources/js/script.js");
41+
42+
fs.copyFileSync(path.join(ROOT_DIR, "styles.css"), path.join(RESOURCES_DIR, "styles.css"));
43+
console.log("✓ Copied styles.css → resources/styles.css");
44+
45+
copyDirSync(path.join(ROOT_DIR, "assets"), path.join(RESOURCES_DIR, "assets"));
46+
console.log("✓ Copied assets/ → resources/assets/");
47+
48+
// Download helper
49+
function downloadFile(url, destPath) {
50+
return new Promise((resolve, reject) => {
51+
if (fs.existsSync(destPath) && fs.statSync(destPath).size > 0) {
52+
resolve();
53+
return;
54+
}
55+
console.log(`Downloading offline dependency: ${path.basename(destPath)}...`);
56+
https.get(url, (res) => {
57+
if (res.statusCode !== 200) {
58+
reject(new Error(`Failed to load ${url} (${res.statusCode})`));
59+
return;
60+
}
61+
const stream = fs.createWriteStream(destPath);
62+
res.pipe(stream);
63+
stream.on("finish", () => {
64+
stream.close();
65+
resolve();
66+
});
67+
}).on("error", reject);
68+
});
69+
}
70+
71+
async function prepareOfflineDependencies() {
72+
console.log("\nStarting Offline Assets Preparation...");
73+
let html = fs.readFileSync(path.join(ROOT_DIR, "index.html"), "utf-8");
74+
75+
// Find all CDN script and link tags
76+
const cdnRegex = /(href|src)="(https:\/\/(?:cdnjs\.cloudflare\.com|cdn\.jsdelivr\.net)\/[^"]+)"/g;
77+
let match;
78+
const downloads = [];
79+
const replacements = [];
80+
81+
while ((match = cdnRegex.exec(html)) !== null) {
82+
const attr = match[1];
83+
const url = match[2];
84+
85+
// Determine local filename - sanitize package version tags or query strings
86+
const urlPath = new URL(url).pathname;
87+
let filename = path.basename(urlPath);
88+
if (url.includes("bootstrap-icons")) {
89+
filename = "bootstrap-icons.min.css";
90+
}
91+
92+
const localDest = path.join(LIBS_DIR, filename);
93+
downloads.push(downloadFile(url, localDest));
94+
95+
// Queue replacement in HTML to point to local libs folder
96+
replacements.push({
97+
original: `${attr}="${url}"`,
98+
replaced: `${attr}="/libs/${filename}"`
99+
});
100+
}
101+
102+
// Also download the relative fonts loaded by bootstrap-icons
103+
const fontDir = path.join(LIBS_DIR, "fonts");
104+
fs.mkdirSync(fontDir, { recursive: true });
105+
downloads.push(downloadFile("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff2", path.join(fontDir, "bootstrap-icons.woff2")));
106+
downloads.push(downloadFile("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff", path.join(fontDir, "bootstrap-icons.woff")));
107+
108+
// Wait for all downloads to finish
109+
try {
110+
await Promise.all(downloads);
111+
console.log("✓ All offline libraries successfully prepared.");
112+
} catch (err) {
113+
console.warn("⚠ Failed to bundle some dependencies offline. Fallback to CDNs will occur.", err.message);
114+
}
115+
116+
// Apply replacements in HTML
117+
replacements.forEach(rep => {
118+
html = html.replace(rep.original, rep.replaced);
119+
});
120+
121+
// Fix relative assets
122+
html = html.replace(/href="assets\//g, 'href="/assets/');
123+
html = html.replace(/href="styles\.css"/g, 'href="/styles.css"');
124+
125+
// Inject Neutralino script tags
126+
html = html.replace(
127+
/<script\s+src="script\.js"\s*><\/script>/i,
128+
'<script src="/js/neutralino.js"></script>\n <script src="/js/main.js"></script>\n <script src="/js/script.js"></script>',
129+
);
130+
131+
// Inject app-info element
132+
html = html.replace(
133+
'<div class="app-container">',
134+
`<div class="app-container">
135+
<div id="neutralino-app">
136+
<div id="neutralino-info"></div>
137+
</div>`,
138+
);
139+
140+
fs.writeFileSync(path.join(RESOURCES_DIR, "index.html"), html, "utf-8");
141+
console.log("✓ Generated resources/index.html (Offline replacements & injections applied)");
142+
console.log("\nDone! Run `npm run dev` to start the desktop app.");
143+
}
144+
145+
prepareOfflineDependencies().catch(console.error);

0 commit comments

Comments
 (0)