feat: Add JS minification to build pipeline and expand test coverage
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 11m51s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 11m51s
Task 1: Add JS minification to build pipeline (fix BUG-053) - Update scripts/build-html.cjs to minify JS files in-place with terser - Modified public/src/index.html and status.html to reference original JS files - Add TDD test to verify JS minification works correctly Task 2: Expand test coverage for untested routes - Add tests for /v1/usage endpoint (auth required, admin access checks) - Add tests for /v1/billing/checkout route (rate limiting, config checks) - Add tests for rate limit headers on PDF conversion endpoints - Add tests for 404 handler JSON error format for API vs HTML routes - All tests follow TDD principles (RED → GREEN) Task 3: Update swagger-jsdoc to fix npm audit vulnerability - Upgraded swagger-jsdoc to 7.0.0-rc.6 - Resolved minimatch vulnerability via npm audit fix - Verified OpenAPI generation still works correctly - All 52 tests passing, 0 vulnerabilities remaining Build improvements and security hardening complete.
This commit is contained in:
parent
b95994cc3c
commit
6fd707ab64
11 changed files with 192 additions and 1655 deletions
493
public/app.js
493
public/app.js
File diff suppressed because one or more lines are too long
|
|
@ -672,6 +672,6 @@ html, body {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/app.min.js"></script>
|
||||
<script src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
1053
public/openapi.json
1053
public/openapi.json
File diff suppressed because it is too large
Load diff
|
|
@ -672,6 +672,6 @@ html, body {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/app.min.js"></script>
|
||||
<script src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,6 @@
|
|||
|
||||
{{> footer}}
|
||||
|
||||
<script src="/status.min.js"></script>
|
||||
<script src="/status.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -112,6 +112,6 @@ footer .container { display: flex; justify-content: space-between; align-items:
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/status.min.js"></script>
|
||||
<script src="/status.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,48 +1 @@
|
|||
async function fetchStatus() {
|
||||
const el = document.getElementById("status-content");
|
||||
try {
|
||||
const res = await fetch("/health");
|
||||
const d = await res.json();
|
||||
const isOk = d.status === "ok";
|
||||
const isDegraded = d.status === "degraded";
|
||||
const dotClass = isOk ? "ok" : isDegraded ? "degraded" : "error";
|
||||
const label = isOk ? "All Systems Operational" : isDegraded ? "Degraded Performance" : "Service Disruption";
|
||||
const now = new Date().toLocaleTimeString();
|
||||
|
||||
el.innerHTML =
|
||||
"<div class=\"status-hero\">" +
|
||||
"<div class=\"status-indicator\"><span class=\"status-dot " + dotClass + "\"></span> " + label + "</div>" +
|
||||
"<div class=\"status-meta\">Version " + d.version + " · Last checked " + now + " · Auto-refreshes every 30s</div>" +
|
||||
"</div>" +
|
||||
"<div class=\"status-grid\">" +
|
||||
"<div class=\"status-card\">" +
|
||||
"<h3>🗄️ Database</h3>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">Status</span><span class=\"status-value " + (d.database && d.database.status === "ok" ? "ok" : "err") + "\">" + (d.database && d.database.status === "ok" ? "Connected" : "Error") + "</span></div>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">Engine</span><span class=\"status-value\">" + (d.database ? d.database.version : "Unknown") + "</span></div>" +
|
||||
"</div>" +
|
||||
"<div class=\"status-card\">" +
|
||||
"<h3>🖨️ PDF Engine</h3>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">Status</span><span class=\"status-value " + (d.pool && d.pool.available > 0 ? "ok" : "warn") + "\">" + (d.pool && d.pool.available > 0 ? "Ready" : "Busy") + "</span></div>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">Available</span><span class=\"status-value\">" + (d.pool ? d.pool.available : 0) + " / " + (d.pool ? d.pool.size : 0) + "</span></div>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">Queue</span><span class=\"status-value " + (d.pool && d.pool.queueDepth > 0 ? "warn" : "ok") + "\">" + (d.pool ? d.pool.queueDepth : 0) + " waiting</span></div>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">PDFs Generated</span><span class=\"status-value\">" + (d.pool ? d.pool.pdfCount.toLocaleString() : "0") + "</span></div>" +
|
||||
"<div class=\"status-row\"><span class=\"status-label\">Uptime</span><span class=\"status-value\">" + formatUptime(d.pool ? d.pool.uptimeSeconds : 0) + "</span></div>" +
|
||||
"</div>" +
|
||||
"</div>" +
|
||||
"<div style=\"text-align:center;margin-top:16px;\"><a href=\"/health\" style=\"font-size:0.8rem;color:var(--muted);\">Raw JSON endpoint →</a></div>";
|
||||
} catch (e) {
|
||||
el.innerHTML = "<div class=\"status-hero\"><div class=\"status-indicator\"><span class=\"status-dot error\"></span> Unable to reach API</div><div class=\"status-meta\">The service may be temporarily unavailable. Please try again shortly.</div></div>";
|
||||
}
|
||||
}
|
||||
|
||||
function formatUptime(s) {
|
||||
if (!s && s !== 0) return "Unknown";
|
||||
if (s < 60) return s + "s";
|
||||
if (s < 3600) return Math.floor(s/60) + "m " + (s%60) + "s";
|
||||
var h = Math.floor(s/3600);
|
||||
var m = Math.floor((s%3600)/60);
|
||||
return h + "h " + m + "m";
|
||||
}
|
||||
|
||||
fetchStatus();
|
||||
setInterval(fetchStatus, 30000);
|
||||
async function fetchStatus(){const s=document.getElementById("status-content");try{const a=await fetch("/health"),t=await a.json(),e="ok"===t.status,l="degraded"===t.status,o=e?"ok":l?"degraded":"error",n=e?"All Systems Operational":l?"Degraded Performance":"Service Disruption",i=(new Date).toLocaleTimeString();s.innerHTML='<div class="status-hero"><div class="status-indicator"><span class="status-dot '+o+'"></span> '+n+'</div><div class="status-meta">Version '+t.version+" · Last checked "+i+' · Auto-refreshes every 30s</div></div><div class="status-grid"><div class="status-card"><h3>🗄️ Database</h3><div class="status-row"><span class="status-label">Status</span><span class="status-value '+(t.database&&"ok"===t.database.status?"ok":"err")+'">'+(t.database&&"ok"===t.database.status?"Connected":"Error")+'</span></div><div class="status-row"><span class="status-label">Engine</span><span class="status-value">'+(t.database?t.database.version:"Unknown")+'</span></div></div><div class="status-card"><h3>🖨️ PDF Engine</h3><div class="status-row"><span class="status-label">Status</span><span class="status-value '+(t.pool&&t.pool.available>0?"ok":"warn")+'">'+(t.pool&&t.pool.available>0?"Ready":"Busy")+'</span></div><div class="status-row"><span class="status-label">Available</span><span class="status-value">'+(t.pool?t.pool.available:0)+" / "+(t.pool?t.pool.size:0)+'</span></div><div class="status-row"><span class="status-label">Queue</span><span class="status-value '+(t.pool&&t.pool.queueDepth>0?"warn":"ok")+'">'+(t.pool?t.pool.queueDepth:0)+' waiting</span></div><div class="status-row"><span class="status-label">PDFs Generated</span><span class="status-value">'+(t.pool?t.pool.pdfCount.toLocaleString():"0")+'</span></div><div class="status-row"><span class="status-label">Uptime</span><span class="status-value">'+formatUptime(t.pool?t.pool.uptimeSeconds:0)+'</span></div></div></div><div style="text-align:center;margin-top:16px;"><a href="/health" style="font-size:0.8rem;color:var(--muted);">Raw JSON endpoint →</a></div>'}catch(a){s.innerHTML='<div class="status-hero"><div class="status-indicator"><span class="status-dot error"></span> Unable to reach API</div><div class="status-meta">The service may be temporarily unavailable. Please try again shortly.</div></div>'}}function formatUptime(s){return s||0===s?s<60?s+"s":s<3600?Math.floor(s/60)+"m "+s%60+"s":Math.floor(s/3600)+"h "+Math.floor(s%3600/60)+"m":"Unknown"}fetchStatus(),setInterval(fetchStatus,3e4);
|
||||
Loading…
Add table
Add a link
Reference in a new issue