Преглед изворни кода

build(packaging): add pkg and other scripts for executable packaging of static resources and server
build(electron): use independent script for electron packaging, pass remote service address from build script

ddx пре 1 месец
родитељ
комит
965c5e8b8d

+ 48 - 0
.build/h5_for_electron.build.linux.arm.js

@@ -0,0 +1,48 @@
+// 执行脚本 taro build --type h5  ,并且把环境变量 TARO_API_URL  传递过去
+
+const { execSync } = require('child_process');
+const fs = require('fs');
+const path = require('path');
+
+const TARO_API_URL = 'http://localhost:6001'; // 远程地址,这里写死,要做成部署后可配置
+const TARO_MQTT_URL ='ws://localhost:8083/mqtt';
+const rootDir = path.join(__dirname, '..');          // 项目根目录
+
+
+execSync(`npm run build:h5`, { cwd: rootDir, stdio: 'inherit', env: { ...process.env, TARO_API_URL ,TARO_MQTT_URL} }, (error, stdout, stderr) => {
+    if (error) {
+        console.error(`Error executing command: ${error.message}`);
+        return;
+    }
+    if (stderr) {
+        console.error(`Command stderr: ${stderr}`);
+        return;
+    }
+    console.log(`Command stdout: ${stdout}`);
+});
+
+function run(cmd) {
+    console.log(`\n>>> ${cmd}`);
+    try {
+        execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
+    } catch (e) {
+        console.error(`命令失败: ${cmd}`, e.message);
+        exit(1);
+    }
+}
+
+try {
+    // run('npm run build:h5');
+    // run('npm run prebuild:arm:linux');
+    fs.copyFileSync(
+        path.join(rootDir, '.build', 'taro.linux-arm64-gnu.node'),
+        path.join(rootDir, 'node_modules', '@tarojs', 'binding', 'taro.linux-arm64-gnu.node')
+    );
+    // run('npm run build:electron:win');
+    run('npm run build:electron:linux');
+    
+    console.log('\n✅ 全部构建完成');
+} catch (err) {
+    console.error('Failed to execute :', err.message);
+    process.exit(1);
+}

+ 27 - 0
.build/h5_for_webserver.build.js

@@ -0,0 +1,27 @@
+// 执行脚本 taro build --type h5  ,并且把环境变量 TARO_API_URL  传递过去
+
+const { execSync } = require('child_process');
+const path = require('path');
+
+const TARO_API_URL = ''; //
+const TARO_MQTT_URL ='ws://localhost:8083/mqtt';
+const rootDir = path.join(__dirname, '..');          // 项目根目录
+
+
+execSync(`taro build --type h5`, { cwd: rootDir, stdio: 'inherit', env: { ...process.env, TARO_API_URL ,TARO_MQTT_URL} }, (error, stdout, stderr) => {
+    if (error) {
+        console.error(`Error executing command: ${error.message}`);
+        return;
+    }
+    if (stderr) {
+        console.error(`Command stderr: ${stderr}`);
+        return;
+    }
+    console.log(`Command stdout: ${stdout}`);
+});
+try {
+    execSync('npm run pkg', { stdio: 'inherit', cwd: rootDir });
+} catch (err) {
+    console.error('Failed to execute npm run pkg:', err.message);
+    process.exit(1);
+}

+ 8 - 0
.github/workflows/build-linux-arm-appimage.yml

@@ -35,6 +35,14 @@ jobs:
           GH_TOKEN: ${{ secrets.GH_TOKEN }}
         run: npm run build:electron:linux
 
+      - name: 切换 Node.js 18 环境
+        uses: actions/setup-node@v4
+        with:
+          node-version: '18'
+
+      - name: 构建 PKG
+        run: npm run pkg
+
       - name: 上传构建结果
         uses: actions/upload-artifact@v4
         with:

+ 2 - 2
config/index.ts

@@ -26,8 +26,8 @@ export default defineConfig<'webpack5'>(async (merge) => {
       API_BASE_URL_FROM_WEBPACK:
         process.env.NODE_ENV === 'development'
           ? '""'
-          : '"http://101.43.219.60:7700"',
-      MQTT_BROKER_URL_FROM_WEBPACK: '"ws://101.43.219.60:8083/mqtt"',
+          : (`"${process.env.TARO_API_URL}"`),
+      MQTT_BROKER_URL_FROM_WEBPACK: `"${process.env.TARO_MQTT_URL}"`,
     },
     copy: {
       patterns: [],

Разлика између датотеке није приказан због своје велике величине
+ 530 - 81
package-lock.json


+ 3 - 1
package.json

@@ -39,7 +39,8 @@
     "build:electron:linux": "electron-builder --config electron-builder.json  --linux",
     "build:android": "node ./.build/build-android.js",
     "e2e": "npx cypress run --headed",
-    "pack:linux": "node ./.build/pack-linux.js"
+    "pack:linux": "node ./.build/pack-linux.js",
+    "pkg": "npx pkg server.js --config pkg.json --no-bytecode --public-packages '*' --public"
   },
   "browserslist": [
     "defaults and fully supports es6-module",
@@ -76,6 +77,7 @@
     "mitt": "^3.0.1",
     "moment": "^2.30.1",
     "mqtt": "^5.14.0",
+    "pkg": "^5.8.1",
     "react": "^18.0.0",
     "react-dom": "^18.0.0",
     "react-intl": "^7.1.11",

+ 9 - 0
pkg.json

@@ -0,0 +1,9 @@
+{
+  "pkg": {
+    "scripts": "server.js",          
+    "assets": ["dist/h5/**/*"],      
+    "targets": ["node18-linux-arm64", "node18-win-x64", "node18-linux-x64"],
+    "outputPath": "dist/pkg-out",         
+    "compress": "GZip"               
+  }
+}

+ 76 - 0
server.js

@@ -0,0 +1,76 @@
+// server.js  —— 零依赖反向代理版
+const http = require('http');
+const https = require('https');
+const fs = require('fs');
+const path = require('path');
+const url = require('url');
+
+const ROOT = path.join(__dirname, 'dist', 'h5');
+const PORT = process.env.PORT || 3000;
+const REMOTE = 'http://101.43.219.60:7700';
+
+const mime = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon' };
+
+/* ------- 代理函数 ------- */
+function proxyToRemote(req, res) {
+    const parsed = url.parse(req.url);          // /dr/xxx?query
+    const path = parsed.path;
+    const target = url.parse(REMOTE);
+    const options = {
+        hostname: target.hostname,
+        port: target.port || (target.protocol === 'https:' ? 443 : 80),
+        path: path + (parsed.search || ''),
+        method: req.method,
+        headers: { ...req.headers }              // 原样带过去
+    };
+    // 删除本地头,避免 host 不匹配
+    delete options.headers.host;
+    delete options.headers.origin;
+    delete options.headers.referer;
+
+    console.log('Options:', options);
+    console.log('Path:', options.path);
+    const proxyReq = (target.protocol === 'https:' ? https : http).request(options, (proxyRes) => {
+        // 把远端响应头原样写给浏览器
+        res.writeHead(proxyRes.statusCode, proxyRes.headers);
+        proxyRes.pipe(res);
+    }).on('error', e => {
+        console.error('[proxy error]', e.message);
+        res.writeHead(502).end('Bad Gateway');
+    });
+    req.pipe(proxyReq);   // 把浏览器发来的 body 也转发过去
+}
+
+/* ------- 静态文件服务函数 ------- */
+function serveStatic(req, res) {
+    let file = decodeURIComponent(req.url.split('?')[0]);
+    file = file === '/' ? '/index.html' : file;
+    const filePath = path.join(ROOT, file);
+    if (!filePath.startsWith(ROOT)) return res.writeHead(403).end('Forbidden');
+    fs.readFile(filePath, (err, data) => {
+        if (err) {
+            if (file !== '/index.html') {
+                fs.readFile(path.join(ROOT, 'index.html'), (e, d) => {
+                    if (e) return res.writeHead(500).end('Server Error');
+                    res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(d);
+                });
+            } else res.writeHead(404).end('Not Found');
+            return;
+        }
+        const ext = path.extname(filePath);
+        res.writeHead(200, { 'Content-Type': mime[ext] || 'application/octet-stream' });
+        res.end(data);
+    });
+}
+
+/* ------- 主入口 ------- */
+http.createServer((req, res) => {
+    // 1. 预检直接 200
+    if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
+
+    // 2. 路径以 /dr 开头 → 代理
+    if (req.url.startsWith('/dr')) { proxyToRemote(req, res); return; }
+
+    // 3. 其它 → 静态文件
+    serveStatic(req, res);
+}).listen(PORT, () => console.log(`> H5 running at http://localhost:${PORT}`));

Неке датотеке нису приказане због велике количине промена