source: Klonkt/deploy/verify.ps1@ b5bae24

main
Last change on this file since b5bae24 was b5bae24, checked in by Robin Genis <roboburr@…>, 3 months ago

reconcile: commit uncommitted live /srv state (playback fix + prefetch, SF favicon, smooth scroll, scripts)

The live working tree /srv/prutfolio was ahead of the bare repo with direct
server edits that had never been committed back. The deploy hook does
checkout -f main, so the next deploy would have reverted the 3 modified
tracked files to f33db01 and lost this work:

  • audio-player.js: robust playback — retry+backoff on network hiccups (no longer skipping immediately) + next-track prefetch (downloads the next track while the current one plays -> ended->next swaps instantly, covering the autoplay lapse). Fix for the sometimes-next-doesn't-play bug.
  • server.js: favicon mark p -> SF (SoundFabrics rebrand). shell.ejs: audio-player.js cache buster ?v=5 -> ?v=6 + favicon ?v=sf.
  • Plus previously untracked project files committed: lenis.min.js + smooth-scroll.js (not yet wired), scripts/ (v9 import/migration), deploy/ docs (DEPLOY.md/backup.sh/nginx/verify.ps1), .well-known/assetlinks.json. audio-player.js.bak.20260614 deliberately NOT committed.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 6.7 KB
RevLine 
[b5bae24]1# PrutCMS v10 — quick smoke test against a running dev server.
2# Usage:
3# 1. In one terminal: npm run dev
4# 2. In another: .\deploy\verify.ps1
5#
6# Hits public + auth-gated endpoints, checks status codes + key strings in
7# response bodies. Doesn't try to upload — just verifies routing, rendering,
8# CSP, and signed-URL surface.
9
10param(
11 [string]$BaseUrl = 'http://localhost:3000'
12)
13
14$ErrorActionPreference = 'Stop'
15$script:pass = 0
16$script:fail = 0
17
18function Check {
19 param([string]$Name, [bool]$Ok, [string]$Detail = '')
20 $msg = if ($Detail) { "$Name -- $Detail" } else { $Name }
21 if ($Ok) {
22 Write-Host "[OK] $msg" -ForegroundColor Green
23 $script:pass++
24 } else {
25 Write-Host "[FAIL] $msg" -ForegroundColor Red
26 $script:fail++
27 }
28}
29
30function Get-Url {
31 param([string]$Path)
32 try {
33 return Invoke-WebRequest -Uri ($BaseUrl + $Path) -UseBasicParsing -SkipHttpErrorCheck -MaximumRedirection 0 -ErrorAction SilentlyContinue
34 } catch {
35 Write-Host "Request failed: $Path -- $($_.Exception.Message)" -ForegroundColor Yellow
36 return $null
37 }
38}
39
40Write-Host "`n=== PrutCMS verify @ $BaseUrl ===`n" -ForegroundColor Cyan
41
42# ── Server up at all? ─────────────────────────────────────────────
43$home = Get-Url '/'
44$serverUp = ($home -ne $null) -and ($home.StatusCode -eq 200 -or $home.StatusCode -eq 302)
45Check 'Server responds on /' $serverUp
46
47if (-not $serverUp) {
48 Write-Host "`nServer not reachable. Is 'npm run dev' running?" -ForegroundColor Red
49 exit 1
50}
51
52# ── Manifest & feeds ──────────────────────────────────────────────
53$mf = Get-Url '/manifest.webmanifest'
54Check 'Manifest responds 200' ($mf -ne $null -and $mf.StatusCode -eq 200)
55$mfJson = $null
56if ($mf -and $mf.Content) {
57 try { $mfJson = $mf.Content | ConvertFrom-Json } catch {}
58}
59Check 'Manifest has scope field' ($mfJson -ne $null -and $mfJson.scope -ne $null)
60Check 'Manifest id starts prutcms-' ($mfJson -ne $null -and $mfJson.id -like 'prutcms-*')
61Check 'Manifest scope ends with /' ($mfJson -ne $null -and $mfJson.scope.EndsWith('/'))
62
63$feed = Get-Url '/feed.xml'
64Check 'RSS /feed.xml: 200 + xml' ($feed -ne $null -and $feed.StatusCode -eq 200 -and $feed.Content -like '*<rss*')
65$atom = Get-Url '/atom.xml'
66Check 'Atom /atom.xml: 200 + xml' ($atom -ne $null -and $atom.StatusCode -eq 200 -and $atom.Content -like '*<feed*')
67$sm = Get-Url '/sitemap.xml'
68Check 'Sitemap (200 or 404)' ($sm -ne $null -and ($sm.StatusCode -eq 200 -or $sm.StatusCode -eq 404))
69
70# ── Search ─────────────────────────────────────────────────────────
71$s = Get-Url '/search?q=test'
72Check 'Search: 200' ($s -ne $null -and $s.StatusCode -eq 200)
73Check 'Search has form input' ($s -ne $null -and $s.Content -like '*name="q"*')
74
75# ── Audio streaming guards ────────────────────────────────────────
76$noToken = Get-Url '/audio/stream/foo.mp3'
77Check 'No token: 403' ($noToken -ne $null -and $noToken.StatusCode -eq 403)
78
79$badToken = Get-Url '/audio/stream/foo.mp3?t=deadbeef&exp=9999999999'
80Check 'Bad token: 403' ($badToken -ne $null -and $badToken.StatusCode -eq 403)
81
82$traverse = Get-Url '/audio/stream/..%2Fevil'
83Check 'Path traversal: 4xx' ($traverse -ne $null -and $traverse.StatusCode -ge 400 -and $traverse.StatusCode -lt 500)
84
85# ── Auth-gated routes preserve ?next= ──────────────────────────────
86$account = Get-Url '/account'
87Check '/account redirects (302)' ($account -ne $null -and $account.StatusCode -eq 302)
88$loc = if ($account) { $account.Headers.Location } else { $null }
89Check 'Redirect contains ?next=' ($loc -ne $null -and $loc -like '*/auth/login?next=*')
90
91$admin = Get-Url '/admin'
92Check '/admin requires auth' ($admin -ne $null -and ($admin.StatusCode -eq 302 -or $admin.StatusCode -eq 403))
93
94# ── Auth pages ─────────────────────────────────────────────────────
95$login = Get-Url '/auth/login'
96Check 'Login page: 200' ($login -ne $null -and $login.StatusCode -eq 200)
97Check "Login has 'Forgot password?'" ($login -ne $null -and $login.Content -like '*Forgot password*')
98
99$reset = Get-Url '/auth/reset-request'
100Check 'Reset-request page: 200' ($reset -ne $null -and $reset.StatusCode -eq 200)
101
102# ── Reserved slugs ─────────────────────────────────────────────────
103$tag = Get-Url '/tag/anything'
104Check '/tag/:tag responds' ($tag -ne $null -and ($tag.StatusCode -eq 200 -or $tag.StatusCode -eq 404))
105
106$users = Get-Url '/users/nonexistent'
107Check '/users/:username 404 when missing' ($users -ne $null -and $users.StatusCode -eq 404)
108
109# ── Prutter (route exists; status depends on enable_prutter + login) ──
110$prutter = Get-Url '/prutter'
111Check '/prutter route present' ($prutter -ne $null -and ($prutter.StatusCode -eq 302 -or $prutter.StatusCode -eq 404))
112
113# ── HTMX bundled locally ──────────────────────────────────────────
114$htmx = Get-Url '/assets/js/htmx.min.js'
115Check 'HTMX file served' ($htmx -ne $null -and $htmx.StatusCode -eq 200)
116Check 'HTMX is the real lib (>20 KB, not the loader stub)' `
117 ($htmx -ne $null -and $htmx.Content.Length -gt 20000)
118
119# ── CSP / security headers ────────────────────────────────────────
120$csp = if ($home) { $home.Headers.'Content-Security-Policy' } else { $null }
121Check 'CSP present' ($csp -ne $null)
122Check 'CSP no longer references unpkg.com' ($csp -ne $null -and $csp -notlike '*unpkg*')
123
124$nosniff = if ($home) { $home.Headers.'X-Content-Type-Options' } else { $null }
125Check 'Helmet active (X-Content-Type-Options: nosniff)' ($nosniff -eq 'nosniff')
126
127# ── Summary ───────────────────────────────────────────────────────
128Write-Host "`n────────────────────────────────────────"
129$summary = "$($script:pass) passed, $($script:fail) failed."
130if ($script:fail -eq 0) {
131 Write-Host $summary -ForegroundColor Green
132} else {
133 Write-Host $summary -ForegroundColor Red
134 exit 1
135}
Note: See TracBrowser for help on using the repository browser.