Initial homepage
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
param(
|
||||
[string]$Root = (Join-Path $PSScriptRoot "..\site"),
|
||||
[string]$PublicRoot = "",
|
||||
[string]$PublicArchive = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Root = (Resolve-Path -LiteralPath $Root).Path
|
||||
|
||||
function Assert-Check {
|
||||
param([bool]$Condition, [string]$Message)
|
||||
if (-not $Condition) { throw "FAIL: $Message" }
|
||||
Write-Host "PASS: $Message"
|
||||
}
|
||||
|
||||
$requiredFiles = @(
|
||||
"index.html",
|
||||
"404.html",
|
||||
"robots.txt",
|
||||
"sitemap.xml",
|
||||
"site.webmanifest",
|
||||
"assets/css/main.css",
|
||||
"assets/js/main.js",
|
||||
"assets/js/site-config.json",
|
||||
"assets/fonts/NotoSerifSC-subset.woff2",
|
||||
"assets/fonts/OFL.txt",
|
||||
"assets/images/avatar.jpg",
|
||||
"assets/images/avatar-frame-animated.png",
|
||||
"assets/images/avatar-frame-mask.svg",
|
||||
"assets/images/avatar-frame-static.png",
|
||||
"assets/images/favicon.svg"
|
||||
)
|
||||
|
||||
foreach ($relative in $requiredFiles) {
|
||||
Assert-Check (Test-Path -LiteralPath (Join-Path $Root $relative) -PathType Leaf) "required file exists: $relative"
|
||||
}
|
||||
|
||||
$index = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "index.html")
|
||||
$notFound = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "404.html")
|
||||
$manifestPath = Join-Path $Root "site.webmanifest"
|
||||
$manifest = Get-Content -Raw -Encoding UTF8 $manifestPath | ConvertFrom-Json
|
||||
$siteConfig = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "assets/js/site-config.json") | ConvertFrom-Json
|
||||
[Uri]$siteUri = $siteConfig.site.url
|
||||
[string]$siteRoot = $siteUri.AbsoluteUri.TrimEnd('/') + '/'
|
||||
$robots = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "robots.txt")
|
||||
[xml]$sitemap = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "sitemap.xml")
|
||||
[xml]$favicon = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "assets/images/favicon.svg")
|
||||
$nginxPath = Join-Path $PSScriptRoot "..\nginx\midctrl.com.example.conf"
|
||||
Assert-Check (Test-Path -LiteralPath $nginxPath -PathType Leaf) "sanitized Nginx example exists"
|
||||
$nginx = Get-Content -Raw -Encoding UTF8 $nginxPath
|
||||
$css = Get-Content -Raw -Encoding UTF8 (Join-Path $Root "assets/css/main.css")
|
||||
$jsPath = Join-Path $Root "assets/js/main.js"
|
||||
$js = Get-Content -Raw -Encoding UTF8 $jsPath
|
||||
$indexScriptVersion = [regex]::Match($index, 'main\.js\?v=([^"'']+)').Groups[1].Value
|
||||
$notFoundScriptVersion = [regex]::Match($notFound, 'main\.js\?v=([^"'']+)').Groups[1].Value
|
||||
$fontVersion = [regex]::Match($css, 'NotoSerifSC-subset\.woff2\?v=([^"'']+)').Groups[1].Value
|
||||
|
||||
Assert-Check ($notFound -match 'href="/assets/images/avatar\.jpg"' -and $notFound -match 'href="/assets/css/main\.css(?:\?[^" ]*)?"' -and $notFound -match 'src="/assets/js/main\.js(?:\?[^" ]*)?"' -and $notFound -match 'src="/assets/images/avatar\.jpg"') "404 assets use root-absolute URLs"
|
||||
Assert-Check ($notFound -match 'class="portrait-image"' -and $notFound -match 'avatar-frame-animated\.png' -and $notFound -notmatch 'axis-line') "404 page uses the current avatar structure"
|
||||
Assert-Check ($manifest.icons[0].src -eq "/assets/images/avatar.jpg") "manifest icon uses root-absolute URL"
|
||||
Assert-Check ($manifest.icons[0].sizes -eq "512x512") "manifest icon size matches the avatar"
|
||||
Assert-Check ([string]$manifest.start_url -eq "/") "manifest starts at the site root"
|
||||
Assert-Check ($siteConfig.site.name -and $siteConfig.site.description -and $siteUri.IsAbsoluteUri -and $siteUri.Scheme -eq "https") "site identity and HTTPS URL are centralized in site-config.json"
|
||||
Assert-Check ($siteConfig.links.Count -gt 0 -and @($siteConfig.links | Where-Object { -not $_.name -or $_.url -notmatch '^https://' }).Count -eq 0) "navigation configuration contains named HTTPS links"
|
||||
Assert-Check ([string]$sitemap.urlset.url.loc -eq $siteRoot -and $robots -match [regex]::Escape("Sitemap: ${siteRoot}sitemap.xml")) "robots and sitemap use the configured canonical homepage"
|
||||
Assert-Check ($index -match 'rel="canonical" href="/"' -and $index -match 'property="og:url" content="/"' -and $js -match 'applySiteMetadata') "HTML keeps portable metadata fallbacks and applies the configured site URL"
|
||||
Assert-Check ([string]$favicon.svg.viewBox -eq "0 0 64 64") "favicon is valid SVG XML"
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$avatar = [System.Drawing.Image]::FromFile((Join-Path $Root "assets/images/avatar.jpg"))
|
||||
try {
|
||||
Assert-Check ($avatar.Width -eq 512 -and $avatar.Height -eq 512) "avatar is 512x512"
|
||||
} finally {
|
||||
$avatar.Dispose()
|
||||
}
|
||||
|
||||
$node = Get-Command node -ErrorAction SilentlyContinue
|
||||
Assert-Check ($null -ne $node) "Node.js is available for JavaScript syntax validation"
|
||||
& $node.Source --check $jsPath
|
||||
Assert-Check ($LASTEXITCODE -eq 0) "main.js passes node --check"
|
||||
|
||||
$renderTargetTest = Join-Path $PSScriptRoot "..\tests\test-render-target-resize.js"
|
||||
& $node.Source $renderTargetTest $jsPath
|
||||
Assert-Check ($LASTEXITCODE -eq 0) "Render Target resize and recovery tests pass"
|
||||
|
||||
Assert-Check ($js -match 'pointFlower\.numFlowers\s*=\s*1600') "particle count remains 1600"
|
||||
Assert-Check (([regex]::Matches($js, 'gl\.drawArrays\(gl\.POINTS')).Count -eq 5) "flower draw call count remains 5"
|
||||
Assert-Check ($js -match 'for\(var i = 0; i < 2; i\+\+\)' -and ([regex]::Matches($js, 'drawEffect\(effectLib\.dirBlur\)').Count -eq 2)) "Bloom retains four directional blur draws"
|
||||
Assert-Check ($js -match 'maximumFrameRate\s*=\s*120') "animation cap remains 120 FPS"
|
||||
Assert-Check ($js -match 'requestAnimationFrame' -and $js -match 'matchMedia\(' -and $js -match 'prefers-reduced-motion') "refresh scheduling and reduced-motion handling are present"
|
||||
Assert-Check ($js -match 'DOMContentLoaded' -and $js -match 'initializeSakura' -and $js -notmatch "window\.addEventListener\('load'") "WebGL starts when the DOM is ready without waiting for fonts or images"
|
||||
Assert-Check ($js -match "addEventListener\('pagehide'" -and $js -match "addEventListener\('pageshow'" -and $js -match 'synchronizeAnimationState') "page lifecycle recovery re-synchronizes the animation loop"
|
||||
Assert-Check ($js -match 'request\.timeout\s*=\s*3000') "navigation configuration has a bounded network timeout"
|
||||
Assert-Check ($indexScriptVersion -and $indexScriptVersion -eq $notFoundScriptVersion -and $indexScriptVersion -eq $fontVersion) "homepage, 404 page, and font use one cache version"
|
||||
Assert-Check ($js -match 'createRenderTargetSet' -and $js -match 'deleteRenderTargetSet') "render target sets are swapped atomically"
|
||||
Assert-Check ($js -match 'normalRenderPixelBudget\s*=\s*10000000' -and $js -match 'constrainedRenderPixelBudget\s*=\s*6000000') "render target pixel budgets are configured"
|
||||
Assert-Check ($js -match 'resizeDebounceMs\s*=\s*140' -and $js -match 'measureCanvasSize' -and $js -match 'commitCanvasSize') "resize measurement and delayed commit are present"
|
||||
Assert-Check ($js -match 'shouldReleaseBeforeAllocate' -and $js -match 'gl\.finish\(\)' -and $js -match 'getResizeCandidates') "high-memory resize and allocation fallback paths are present"
|
||||
Assert-Check ($js -notmatch 'gl\.enable\(gl\.TEXTURE_2D\)' -and $js -notmatch 'gl\.disable\(gl\.TEXTURE_2D\)' -and $js -notmatch 'gl\.POINT\b') "invalid WebGL capability enums are absent"
|
||||
Assert-Check ($nginx -match 'root /var/www/midctrl\.com;' -and $nginx -match 'try_files \$uri \$uri/ =404') "Nginx serves only the dedicated static web root"
|
||||
Assert-Check ($nginx -match 'location ~\* \^/assets/\(\?:css\|js\)/' -and $nginx -match 'expires -1') "unversioned CSS and JavaScript revalidate"
|
||||
Assert-Check ($nginx -match 'location \^~ /assets/fonts/' -and $nginx -match 'Cache-Control "public, immutable"') "self-hosted fonts use long-lived immutable caching"
|
||||
Assert-Check ($nginx -notmatch 'location\s+/[a-f0-9]{16,}' -and $nginx -notmatch 'proxy_pass\s+http://127\.0\.0\.1:\d+') "public Nginx example omits host-private proxy details"
|
||||
Assert-Check ($js -match 'frameRequestGeneration' -and $js -match 'cancelScheduledAnimationFrame') "animation loop guards stale callbacks after context loss"
|
||||
Assert-Check ((Get-Item -LiteralPath (Join-Path $Root "assets/fonts/NotoSerifSC-subset.woff2")).Length -gt 1000 -and (Get-Content -Raw -Encoding UTF8 (Join-Path $Root "assets/fonts/OFL.txt")) -match 'SIL Open Font License') "Noto Serif SC subset and license are present"
|
||||
|
||||
$allowedPublic = @(
|
||||
"index.html",
|
||||
"404.html",
|
||||
"robots.txt",
|
||||
"sitemap.xml",
|
||||
"site.webmanifest",
|
||||
"assets/css/main.css",
|
||||
"assets/js/main.js",
|
||||
"assets/js/site-config.json",
|
||||
"assets/fonts/NotoSerifSC-subset.woff2",
|
||||
"assets/fonts/OFL.txt",
|
||||
"assets/images/avatar.jpg",
|
||||
"assets/images/avatar-frame-animated.png",
|
||||
"assets/images/avatar-frame-mask.svg",
|
||||
"assets/images/avatar-frame-static.png",
|
||||
"assets/images/favicon.svg"
|
||||
) | Sort-Object
|
||||
|
||||
if (-not $PublicRoot) {
|
||||
$PublicRoot = $Root
|
||||
}
|
||||
|
||||
if ($PublicRoot) {
|
||||
$PublicRoot = (Resolve-Path -LiteralPath $PublicRoot).Path
|
||||
$actualPublic = @(Get-ChildItem -LiteralPath $PublicRoot -Recurse -File | ForEach-Object {
|
||||
$_.FullName.Substring($PublicRoot.TrimEnd('\').Length + 1).Replace('\', '/')
|
||||
} | Sort-Object)
|
||||
Assert-Check (($actualPublic -join "`n") -eq ($allowedPublic -join "`n")) "public bundle contains only web-root files"
|
||||
}
|
||||
|
||||
if ($PublicArchive) {
|
||||
$PublicArchive = (Resolve-Path -LiteralPath $PublicArchive).Path
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$archive = [System.IO.Compression.ZipFile]::OpenRead($PublicArchive)
|
||||
try {
|
||||
$archiveFiles = @($archive.Entries | Where-Object { $_.Name } | ForEach-Object {
|
||||
$_.FullName.Replace('\', '/').TrimStart('/')
|
||||
} | Sort-Object)
|
||||
Assert-Check (($archiveFiles -join "`n") -eq ($allowedPublic -join "`n")) "public ZIP contains only web-root files"
|
||||
} finally {
|
||||
$archive.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Production checks complete."
|
||||
Reference in New Issue
Block a user