From 4ca4abff7bbe8ade47b321b8db302004591a29e1 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Sat, 15 Aug 2026 17:07:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Sense=20Windows=20?= =?UTF-8?q?=E5=8C=85=E7=99=BD=E5=B1=8F=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sense/README-WINDOWS.md | 3 +- Sense/scripts/build/assert-web-assets.ps1 | 37 +++++++++++++++++++++++ Sense/scripts/build/test-package.ps1 | 1 + Sense/tests/package/run-tests.ps1 | 13 ++++++-- Sense/ui/vue.config.js | 15 +++++---- 5 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 Sense/scripts/build/assert-web-assets.ps1 diff --git a/Sense/README-WINDOWS.md b/Sense/README-WINDOWS.md index 1ece1fe..cb4295d 100644 --- a/Sense/README-WINDOWS.md +++ b/Sense/README-WINDOWS.md @@ -133,6 +133,7 @@ Demo 启动窗口按 `Ctrl+C` 停止;窗口不可用时执行 `stop-sense.bat - `External MediaMTX Control API is unreachable`:启动外部实例并确认 API 只监听回环地址。 - `migration failed`:不要跳过;先备份,保留错误输出,核对数据库账号权限和版本。 - 网页返回 404:检查 `web\index.html` 与 `SENSE_WEB_ROOT=web`,不要把源码目录或 `node_modules` 放进包。 +- 网页返回 200 但白屏:在浏览器开发者工具检查 JS/CSS 是否 404;正式包的构建审计会逐项核对 `web\index.html` 引用的本地资源,缺失时拒绝生成交付包。 ## 8. 构建交付包 @@ -153,4 +154,4 @@ Sense\scripts\build\build-windows.ps1 -MediaMTXPath D:\approved\mediamtx.exe - `Sense\dist\sense-windows-amd64\` - `Sense\dist\sense-windows-amd64.zip` -构建末尾会审计包内容:拒绝 `node_modules`、嵌套 `dist`、Git/缓存目录、数据库/备份文件、非空秘密字段、常见默认密码和私钥标记。`dist` 为可重建产物,不提交 Git。 +构建末尾会审计包内容:逐项核对 `web\index.html` 引用的本地 JS/CSS,并拒绝 `node_modules`、嵌套 `dist`、Git/缓存目录、数据库/备份文件、非空秘密字段、常见默认密码和私钥标记。`dist` 为可重建产物,不提交 Git。 diff --git a/Sense/scripts/build/assert-web-assets.ps1 b/Sense/scripts/build/assert-web-assets.ps1 new file mode 100644 index 0000000..2a9b0b1 --- /dev/null +++ b/Sense/scripts/build/assert-web-assets.ps1 @@ -0,0 +1,37 @@ +param([Parameter(Mandatory = $true)][string]$WebRoot) +Set-StrictMode -Version 3.0 +$ErrorActionPreference = 'Stop' + +$root = [IO.Path]::GetFullPath($WebRoot) +$indexPath = Join-Path $root 'index.html' +if (-not (Test-Path -LiteralPath $indexPath -PathType Leaf)) { + throw "Web index not found: $indexPath" +} + +$rootPrefix = $root.TrimEnd('\') + '\' +$html = Get-Content -LiteralPath $indexPath -Raw +$references = [regex]::Matches($html, '(?i)(?:src|href)\s*=\s*["''](?[^"'']+)["'']') +$checked = 0 +foreach ($match in $references) { + $assetReference = $match.Groups['path'].Value.Trim() + if (-not $assetReference -or $assetReference.StartsWith('//') -or $assetReference -match '^[a-z][a-z0-9+.-]*:') { + continue + } + $assetPath = ($assetReference -split '[?#]', 2)[0] + if ([IO.Path]::GetExtension($assetPath).ToLowerInvariant() -notin @('.js', '.css')) { + continue + } + $relative = [Uri]::UnescapeDataString($assetPath).TrimStart('/').Replace('/', '\') + if (-not $relative) { throw "Web index contains an empty local asset path: $assetReference" } + $resolved = [IO.Path]::GetFullPath((Join-Path $root $relative)) + if (-not $resolved.StartsWith($rootPrefix, [StringComparison]::OrdinalIgnoreCase)) { + throw "Web index asset escapes the web root: $assetReference" + } + if (-not (Test-Path -LiteralPath $resolved -PathType Leaf)) { + throw "Web index references missing local asset: $assetReference" + } + $checked++ +} + +if ($checked -eq 0) { throw 'Web index does not reference any local JavaScript or CSS assets.' } +Write-Host "Sense web asset audit passed: $checked local references." diff --git a/Sense/scripts/build/test-package.ps1 b/Sense/scripts/build/test-package.ps1 index 442fa91..2035141 100644 --- a/Sense/scripts/build/test-package.ps1 +++ b/Sense/scripts/build/test-package.ps1 @@ -15,6 +15,7 @@ $required = @( foreach ($relative in $required) { if (-not (Test-Path -LiteralPath (Join-Path $root $relative))) { throw "Package is missing required path: $relative" } } +& (Join-Path $PSScriptRoot 'assert-web-assets.ps1') -WebRoot (Join-Path $root 'web') $forbiddenDirectories = Get-ChildItem -LiteralPath $root -Recurse -Directory | Where-Object { $_.Name -in @('node_modules', '.git', 'dist', '.cache') } if ($forbiddenDirectories) { throw "Package contains forbidden build directory: $($forbiddenDirectories[0].FullName)" } $forbiddenFiles = Get-ChildItem -LiteralPath $root -Recurse -File | Where-Object { $_.Extension -in @('.db', '.sqlite', '.sqlite3', '.dump', '.bak') } diff --git a/Sense/tests/package/run-tests.ps1 b/Sense/tests/package/run-tests.ps1 index 9195279..3b2c269 100644 --- a/Sense/tests/package/run-tests.ps1 +++ b/Sense/tests/package/run-tests.ps1 @@ -28,11 +28,20 @@ foreach ($name in $script:SenseAllowedEnvironment) { [Environment]::SetEnvironmentVariable($name, $null, 'Process') } try { - New-Item -ItemType Directory -Path (Join-Path $temporary 'config'), (Join-Path $temporary 'web'), (Join-Path $temporary 'bin') | Out-Null - [IO.File]::WriteAllText((Join-Path $temporary 'web\index.html'), 'sense', (New-Object Text.UTF8Encoding($false))) + New-Item -ItemType Directory -Path (Join-Path $temporary 'config'), (Join-Path $temporary 'web'), (Join-Path $temporary 'web\js'), (Join-Path $temporary 'bin') | Out-Null + $webIndex = '
' + [IO.File]::WriteAllText((Join-Path $temporary 'web\index.html'), $webIndex, (New-Object Text.UTF8Encoding($false))) + [IO.File]::WriteAllText((Join-Path $temporary 'web\js\runtime.fixture.js'), 'fixture', (New-Object Text.UTF8Encoding($false))) [IO.File]::WriteAllText((Join-Path $temporary 'bin\mediamtx.exe'), 'fixture', (New-Object Text.UTF8Encoding($false))) [IO.File]::WriteAllText((Join-Path $temporary 'config\mediamtx.yml'), 'api: true', (New-Object Text.UTF8Encoding($false))) + $webAssetAudit = Join-Path $senseRoot 'scripts\build\assert-web-assets.ps1' + & $webAssetAudit -WebRoot (Join-Path $temporary 'web') + Assert-True $true 'web asset audit must accept existing local references' + Remove-Item -LiteralPath (Join-Path $temporary 'web\js\runtime.fixture.js') + Assert-Throws { & $webAssetAudit -WebRoot (Join-Path $temporary 'web') } 'missing local asset' 'web asset audit must reject missing runtime files' + [IO.File]::WriteAllText((Join-Path $temporary 'web\js\runtime.fixture.js'), 'fixture', (New-Object Text.UTF8Encoding($false))) + $listener = New-Object Net.Sockets.TcpListener([Net.IPAddress]::Loopback, 0) $listener.Start() $dbPort = ([Net.IPEndPoint]$listener.LocalEndpoint).Port diff --git a/Sense/ui/vue.config.js b/Sense/ui/vue.config.js index 6589e04..aa01667 100644 --- a/Sense/ui/vue.config.js +++ b/Sense/ui/vue.config.js @@ -107,14 +107,6 @@ module.exports = { config .when(process.env.NODE_ENV !== 'development', config => { - config - .plugin('ScriptExtHtmlWebpackPlugin') - .after('html') - .use('script-ext-html-webpack-plugin', [{ - // `runtime` must same as runtimeChunk name. default is `runtime` - inline: /runtime\..*\.js$/ - }]) - .end() config .optimization.splitChunks({ chunks: 'all', @@ -145,6 +137,13 @@ module.exports = { }, css: { loaderOptions: { + css: { + // Preserve GoAdmin's :export variables as JavaScript values with css-loader 6. + // ICSS mode does not rename ordinary global or component class selectors. + modules: { + mode: 'icss' + } + }, less: { modifyVars: { // less vars,customize ant design theme