From b05098926c64e0ecf70f99b8fea6b6981c82977e Mon Sep 17 00:00:00 2001 From: mrlee <470349233@qq.com> Date: Fri, 2 Oct 2026 15:32:37 +0800 Subject: [PATCH] feat: bundle multi-arch nginx for native fnOS stage 2 --- README.md | 33 +++++++++++++++--- app-src/internal/nginx/generate.go | 6 ++++ build.ps1 | 54 ++++++++++++++++++++++++++++++ havline/cmd/install_callback | 14 +++++++- havline/cmd/main | 4 +++ havline/manifest | 4 +-- 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1f01dc7..59572a7 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,35 @@ 构建过程不再引用 `F:\Fonu-main\havline`。 -阶段一范围: +阶段二范围: - Go 主程序原生运行 - 统一网关 `/app/havline` - Unix Socket `app.sock` - `cmd/main` 启动、停止和状态检查 - amd64 与 arm64 双架构二进制 +- Nginx amd64 与 arm64 双架构二进制 +- Nginx 依赖库与 `mime.types` +- HTTP 18080 与 HTTPS 9443 当前不包含: -- Nginx 二进制 - frpc - cloudflared 这些依赖将在后续阶段加入。 +## 构建前置条件 + +构建脚本需要本地存在用于提取 Nginx 的 Havline 镜像: + +```text +jayhub.top/mrlee/havline:1.9.0 amd64 +havline:1.9.0 arm64 +``` + +如果本地没有对应架构镜像,先加载 amd64 与 arm64 镜像归档。 + ## 构建 ```powershell @@ -43,5 +56,17 @@ dist/havline-native-1.9.0.fpk 构建脚本会: 1. 使用 `VITE_BASE_PATH=/app/havline/` 构建前端; -2. 交叉编译 amd64 与 arm64 Go 二进制; -3. 调用 `fnpack build` 打包。 +2. 从本地镜像提取 amd64 与 arm64 Nginx 及依赖库; +3. 交叉编译 amd64 与 arm64 Go 二进制; +4. 调用 `fnpack build` 打包。 + +## 真机注意 + +当前 Nginx 来自 Debian 12,依赖目标系统 glibc。飞牛设备建议满足: + +```text +glibc >= 2.36 +``` + +如果目标设备 glibc 版本较低,需要改为静态编译 Nginx 或打包完整 +loader 与 libc。 diff --git a/app-src/internal/nginx/generate.go b/app-src/internal/nginx/generate.go index 32d844b..ba47a5d 100644 --- a/app-src/internal/nginx/generate.go +++ b/app-src/internal/nginx/generate.go @@ -26,6 +26,12 @@ http { include ` + absNginxPath(cfg.NginxMimeTypes) + `; default_type application/octet-stream; + client_body_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "client_body_temp")) + `; + proxy_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "proxy_temp")) + `; + fastcgi_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "fastcgi_temp")) + `; + uwsgi_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "uwsgi_temp")) + `; + scgi_temp_path ` + absNginxPath(filepath.Join(cfg.NginxDir(), "temp", "scgi_temp")) + `; + log_format havline_access '$time_iso8601 $host $server_port $request_method $request_uri $status $request_time $remote_addr $upstream_addr $request_length $bytes_sent'; access_log ` + absNginxPath(filepath.Join(cfg.LogsDir(), "access.log")) + ` havline_access; diff --git a/build.ps1 b/build.ps1 index 9317256..21ab7e6 100644 --- a/build.ps1 +++ b/build.ps1 @@ -21,6 +21,55 @@ function Copy-Tree { } } +function Resolve-DockerCli { + if ($env:DOCKER_BIN) { return $env:DOCKER_BIN } + $command = Get-Command docker -ErrorAction SilentlyContinue + if ($command) { return $command.Source } + $default = "C:\Program Files\Docker\Docker\resources\bin\docker.exe" + if (Test-Path -LiteralPath $default) { return $default } + throw "找不到 docker 命令" +} + +function Export-Nginx { + param( + [string]$DockerBin, + [string]$Platform, + [string]$Arch, + [string]$Image, + [string]$OutputDir + ) + + $archInfo = (& $DockerBin image inspect $Image --format '{{.Architecture}}' 2>$null) + if ($LASTEXITCODE -ne 0) { + throw "找不到 Nginx 来源镜像:$Image" + } + if ($archInfo.Trim() -ne $Arch) { + throw "镜像架构不匹配:$Image 是 $($archInfo.Trim()),需要 $Arch" + } + + $archDir = Join-Path $OutputDir $Arch + New-Item -ItemType Directory -Force $archDir | Out-Null + $script = @' +set -e +mkdir -p /out/lib +cp /usr/sbin/nginx /out/nginx +cp /etc/nginx/mime.types /out/mime.types +ldd /usr/sbin/nginx | awk '/=> \// {print $3} /^\// {print $1}' | sort -u | while read lib; do + case "$(basename "$lib")" in + libc.so.6|ld-linux-*.so.*) continue ;; + esac + cp "$lib" /out/lib/ +done +'@ + + & $DockerBin run --rm --platform $Platform -v "${archDir}:/out" --entrypoint sh $Image -c $script + if ($LASTEXITCODE -ne 0) { + throw "提取 $Arch Nginx 失败,退出码:$LASTEXITCODE" + } + + Copy-Item -LiteralPath (Join-Path $archDir "mime.types") -Destination (Join-Path $OutputDir "mime.types") -Force +} + if (-not (Test-Path -LiteralPath $project)) { throw "找不到 Native 应用工程:$project" } @@ -67,6 +116,11 @@ if ($LASTEXITCODE -ge 8) { Copy-Tree -Source (Join-Path $mainRepo "migrations") -Destination (Join-Path $stage "app\migrations") Copy-Tree -Source (Join-Path $mainRepo "web\assets\image") -Destination (Join-Path $stage "app\web\assets\image") +$serverDir = Join-Path $stage "app\server" +$dockerBin = Resolve-DockerCli +Export-Nginx -DockerBin $dockerBin -Platform "linux/amd64" -Arch "amd64" -Image "jayhub.top/mrlee/havline:$version" -OutputDir $serverDir +Export-Nginx -DockerBin $dockerBin -Platform "linux/arm64" -Arch "arm64" -Image "havline:$version" -OutputDir $serverDir + $env:CGO_ENABLED = "0" $env:GOOS = "linux" diff --git a/havline/cmd/install_callback b/havline/cmd/install_callback index 7774e12..28ad79a 100644 --- a/havline/cmd/install_callback +++ b/havline/cmd/install_callback @@ -17,6 +17,18 @@ if [ ! -f "$BIN" ]; then exit 1 fi -chmod +x "$BIN" +NGINX_BIN="$TRIM_APPDEST/server/$ARCH_DIR/nginx" +if [ ! -f "$NGINX_BIN" ]; then + echo "找不到 Nginx 二进制:$NGINX_BIN" > "$TRIM_TEMP_LOGFILE" + exit 1 +fi + +MIME_TYPES="$TRIM_APPDEST/server/mime.types" +if [ ! -f "$MIME_TYPES" ]; then + echo "找不到 Nginx mime.types:$MIME_TYPES" > "$TRIM_TEMP_LOGFILE" + exit 1 +fi + +chmod +x "$BIN" "$NGINX_BIN" echo "Havline 数据目录已准备:$TRIM_PKGVAR" exit 0 diff --git a/havline/cmd/main b/havline/cmd/main index f9b373b..3b67de6 100644 --- a/havline/cmd/main +++ b/havline/cmd/main @@ -58,8 +58,12 @@ start_app() { export HAVLINE_GATEWAY_SOCKET="$SOCKET_PATH" export HAVLINE_GATEWAY_PREFIX="/app/havline" export HAVLINE_UPDATER_SOCKET="" + export HAVLINE_NGINX_BIN="$TRIM_APPDEST/server/$arch/nginx" + export HAVLINE_NGINX_MIME_TYPES="$TRIM_APPDEST/server/mime.types" + export HAVLINE_NGINX_PID="$TRIM_PKGVAR/nginx/nginx.pid" export HAVLINE_NGINX_HTTP_PORT="${wizard_nginx_http_port:-18080}" export HAVLINE_NGINX_HTTPS_PORT="${wizard_nginx_https_port:-9443}" + export LD_LIBRARY_PATH="$TRIM_APPDEST/server/$arch/lib:${LD_LIBRARY_PATH:-}" export TZ="${wizard_timezone:-Asia/Shanghai}" if [ -n "${wizard_initial_admin_password:-}" ]; then diff --git a/havline/manifest b/havline/manifest index 2aec020..da29e66 100644 --- a/havline/manifest +++ b/havline/manifest @@ -1,7 +1,7 @@ appname = havline version = 1.9.0 display_name = Havline -desc = NAS 公网访问管理:原生应用阶段一 +desc = NAS 公网访问管理:原生应用阶段二 platform = all source = thirdparty maintainer = Havline @@ -11,4 +11,4 @@ desktop_uidir = ui desktop_applaunchname = havline.main ctl_stop = true os_min_version = 1.1.3100 -changelog = 原生应用阶段一:Go 主程序、统一网关和启停脚本 +changelog = 原生应用阶段二:Nginx amd64/arm64 双架构打包