Add git commit info to UI (bottom right)

This commit is contained in:
2025-12-27 17:41:00 +11:00
parent 1e18f45f09
commit 7560b9daad
4 changed files with 1162 additions and 934 deletions

View File

@@ -1,14 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="Professional stress testing tool for simulating realistic traffic to production websites">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Professional stress testing tool for simulating realistic traffic to production websites"
/>
<title>Beyond Cloud Technology - Website Stress Test</title>
<link rel="icon" type="image/png" href="Logo.png">
<link rel="stylesheet" href="styles.css">
<link rel="icon" type="image/png" href="Logo.png" />
<link rel="stylesheet" href="styles.css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
</head>
@@ -18,21 +19,33 @@
<header class="header">
<div class="header-content">
<h1 class="title">
<img src="Logo.png" alt="BCT Logo" class="title-icon">
<img src="Logo.png" alt="BCT Logo" class="title-icon" />
Beyond Cloud Technology - Website Stress Test
</h1>
<p class="subtitle">Simulate realistic traffic patterns to test your production websites</p>
<a href="https://www.youtube.com/@beyondcloudtechnology" target="_blank" rel="noopener noreferrer"
class="youtube-link">
<p class="subtitle">
Simulate realistic traffic patterns to test your production websites
</p>
<a
href="https://www.youtube.com/@beyondcloudtechnology"
target="_blank"
rel="noopener noreferrer"
class="youtube-link"
>
<svg class="youtube-icon" viewBox="0 0 24 24" fill="currentColor">
<path
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
/>
</svg>
Watch on YouTube @beyondcloudtechnology
</a>
</div>
<div class="header-controls">
<button type="button" class="btn btn-secondary btn-sm" id="themeToggle" title="Toggle Theme">
<button
type="button"
class="btn btn-secondary btn-sm"
id="themeToggle"
title="Toggle Theme"
>
🌓 Theme
</button>
</div>
@@ -52,10 +65,15 @@
<option value="heavy">🔴 Heavy Load (500 users, 120s)</option>
<option value="spike">💥 Spike Test (200 users, burst)</option>
</select>
<button type="button" class="btn btn-secondary" id="saveConfigBtn">💾 Save Current Config</button>
<button type="button" class="btn btn-secondary" id="saveConfigBtn">
💾 Save Current Config
</button>
</div>
<div class="keyboard-shortcuts">
<small><strong>Keyboard Shortcuts:</strong> S = Start | P = Pause/Resume | X = Stop</small>
<small
><strong>Keyboard Shortcuts:</strong> S = Start | P = Pause/Resume |
X = Stop</small
>
</div>
</div>
@@ -72,7 +90,13 @@
<!-- Target URL -->
<div class="form-group">
<label class="form-label" for="targetUrl">Target URL</label>
<input type="url" id="targetUrl" class="form-input" placeholder="https://example.com" required>
<input
type="url"
id="targetUrl"
class="form-input"
placeholder="https://example.com"
required
/>
</div>
<!-- Number of Users -->
@@ -81,7 +105,14 @@
Concurrent Users
<span class="range-value" id="userCountValue">100</span>
</label>
<input type="range" id="userCount" class="form-range" min="1" max="5000" value="100">
<input
type="range"
id="userCount"
class="form-range"
min="1"
max="5000"
value="100"
/>
</div>
<!-- Test Duration -->
@@ -90,12 +121,22 @@
Duration (seconds)
<span class="range-value" id="durationValue">60</span>
</label>
<input type="range" id="duration" class="form-range" min="10" max="600" value="60" step="10">
<input
type="range"
id="duration"
class="form-range"
min="10"
max="600"
value="60"
step="10"
/>
</div>
<!-- Traffic Pattern -->
<div class="form-group">
<label class="form-label" for="trafficPattern">Traffic Pattern</label>
<label class="form-label" for="trafficPattern"
>Traffic Pattern</label
>
<select id="trafficPattern" class="form-select">
<option value="steady">Steady Load (Constant RPS)</option>
<option value="burst">Burst Mode (Traffic Spikes)</option>
@@ -107,21 +148,40 @@
<!-- Crawler Mode -->
<div class="form-group crawler-section">
<label class="checkbox-label">
<input type="checkbox" id="crawlerEnabled" class="form-checkbox">
<input
type="checkbox"
id="crawlerEnabled"
class="form-checkbox"
/>
<span>🕷️ Enable Crawler Mode (Simulate Real Users)</span>
</label>
<small class="help-text">Randomly navigate through website links like real users</small>
<small class="help-text"
>Randomly navigate through website links like real users</small
>
</div>
<!-- Crawler Settings (shown when enabled) -->
<div class="crawler-settings" id="crawlerSettings" style="display: none;">
<div
class="crawler-settings"
id="crawlerSettings"
style="display: none"
>
<div class="form-group">
<label class="form-label" for="crawlDepth">
Crawl Depth
<span class="range-value" id="crawlDepthValue">2</span>
</label>
<input type="range" id="crawlDepth" class="form-range" min="1" max="5" value="2">
<small class="help-text">Maximum number of page hops per user</small>
<input
type="range"
id="crawlDepth"
class="form-range"
min="1"
max="5"
value="2"
/>
<small class="help-text"
>Maximum number of page hops per user</small
>
</div>
<div class="form-group">
@@ -129,14 +189,27 @@
Links Per Page
<span class="range-value" id="linksPerPageValue">10</span>
</label>
<input type="range" id="linksPerPage" class="form-range" min="1" max="50" value="10">
<small class="help-text">Maximum links to extract from each page</small>
<input
type="range"
id="linksPerPage"
class="form-range"
min="1"
max="50"
value="10"
/>
<small class="help-text"
>Maximum links to extract from each page</small
>
</div>
</div>
<!-- Advanced Options Accordion -->
<div class="accordion">
<button type="button" class="accordion-header" id="advancedToggle">
<button
type="button"
class="accordion-header"
id="advancedToggle"
>
<span>Advanced Options</span>
<span class="accordion-icon"></span>
</button>
@@ -155,16 +228,26 @@
<!-- Request Headers -->
<div class="form-group">
<label class="form-label" for="customHeaders">Custom Headers (JSON)</label>
<textarea id="customHeaders" class="form-textarea"
placeholder='{"Authorization": "Bearer token", "Content-Type": "application/json"}'></textarea>
<label class="form-label" for="customHeaders"
>Custom Headers (JSON)</label
>
<textarea
id="customHeaders"
class="form-textarea"
placeholder='{"Authorization": "Bearer token", "Content-Type": "application/json"}'
></textarea>
</div>
<!-- Request Body -->
<div class="form-group">
<label class="form-label" for="requestBody">Request Body (JSON)</label>
<textarea id="requestBody" class="form-textarea"
placeholder='{"key": "value"}'></textarea>
<label class="form-label" for="requestBody"
>Request Body (JSON)</label
>
<textarea
id="requestBody"
class="form-textarea"
placeholder='{"key": "value"}'
></textarea>
</div>
<!-- Think Time -->
@@ -173,9 +256,18 @@
Think Time (ms)
<span class="range-value" id="thinkTimeValue">1000</span>
</label>
<input type="range" id="thinkTime" class="form-range" min="0" max="5000" value="1000"
step="100">
<small class="help-text">Delay between requests per user</small>
<input
type="range"
id="thinkTime"
class="form-range"
min="0"
max="5000"
value="1000"
step="100"
/>
<small class="help-text"
>Delay between requests per user</small
>
</div>
</div>
</div>
@@ -201,9 +293,20 @@
<!-- Control Buttons -->
<div class="btn-group mt-4">
<button type="button" class="btn btn-success" id="startBtn">▶️ Start Test</button>
<button type="button" class="btn btn-warning" id="pauseBtn" disabled>⏸️ Pause</button>
<button type="button" class="btn btn-danger" id="stopBtn" disabled>⏹️ Stop</button>
<button type="button" class="btn btn-success" id="startBtn">
▶️ Start Test
</button>
<button
type="button"
class="btn btn-warning"
id="pauseBtn"
disabled
>
⏸️ Pause
</button>
<button type="button" class="btn btn-danger" id="stopBtn" disabled>
⏹️ Stop
</button>
</div>
<!-- Quick Stats -->
@@ -316,7 +419,10 @@
</div>
<!-- User/Error Correlation Chart (Full Width) -->
<div class="chart-container" style="height: 350px; margin-top: var(--spacing-lg);">
<div
class="chart-container"
style="height: 350px; margin-top: var(--spacing-lg)"
>
<canvas id="userErrorChart"></canvas>
</div>
</div>
@@ -346,15 +452,19 @@
</div>
<!-- Results Panel -->
<div class="panel full-width" id="resultsPanel" style="display: none;">
<div class="panel full-width" id="resultsPanel" style="display: none">
<div class="panel-header">
<div class="panel-icon">📈</div>
<h2 class="panel-title">Test Results</h2>
</div>
<div class="btn-group mb-3">
<button type="button" class="btn btn-primary" id="exportJsonBtn">💾 Export as JSON</button>
<button type="button" class="btn btn-primary" id="exportCsvBtn">📄 Export as CSV</button>
<button type="button" class="btn btn-primary" id="exportJsonBtn">
💾 Export as JSON
</button>
<button type="button" class="btn btn-primary" id="exportCsvBtn">
📄 Export as CSV
</button>
</div>
<table class="results-table">
@@ -377,20 +487,26 @@
<p>&copy; 2025 Beyond Cloud Technology. All rights reserved.</p>
</footer>
<!-- Git Commit Info -->
<div id="gitInfo" class="git-info-pill" style="display: none">
<span class="git-commit" id="gitCommit"></span>
<span class="git-separator"></span>
<span class="git-date" id="gitDate"></span>
</div>
<script src="script.js"></script>
<script>
// Show/hide crawler settings based on checkbox
document.addEventListener('DOMContentLoaded', () => {
const crawlerCheckbox = document.getElementById('crawlerEnabled');
const crawlerSettings = document.getElementById('crawlerSettings');
document.addEventListener("DOMContentLoaded", () => {
const crawlerCheckbox = document.getElementById("crawlerEnabled");
const crawlerSettings = document.getElementById("crawlerSettings");
if (crawlerCheckbox && crawlerSettings) {
crawlerCheckbox.addEventListener('change', (e) => {
crawlerSettings.style.display = e.target.checked ? 'block' : 'none';
crawlerCheckbox.addEventListener("change", (e) => {
crawlerSettings.style.display = e.target.checked ? "block" : "none";
});
}
});
</script>
</body>
</html>

View File

@@ -37,6 +37,26 @@ function getRandomUserAgent() {
return CONFIG.userAgents[Math.floor(Math.random() * CONFIG.userAgents.length)];
}
const { exec } = require('child_process');
// Helper to get git info
const getGitInfo = () => {
return new Promise((resolve) => {
exec('git rev-parse --short HEAD && git log -1 --format=%cd --date=relative', (err, stdout) => {
if (err) {
console.error('Error fetching git info:', err);
resolve({ commit: 'Unknown', date: 'Unknown' });
return;
}
const parts = stdout.trim().split('\n');
resolve({
commit: parts[0] || 'Unknown',
date: parts[1] || 'Unknown'
});
});
});
};
// Create the proxy server
const server = http.createServer((req, res) => {
// Handle CORS preflight requests
@@ -47,6 +67,16 @@ const server = http.createServer((req, res) => {
return;
}
// Handle Git Info request
if (req.url === '/git-info' && req.method === 'GET') {
handleCORS(res);
getGitInfo().then(info => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(info));
});
return;
}
// Only allow POST requests to the proxy
if (req.method !== 'POST') {
res.writeHead(405, { 'Content-Type': 'application/json' });
@@ -194,7 +224,7 @@ function handleProxyRequest(proxyRequest, clientRes) {
// Add CORS headers to response
function handleCORS(res) {
res.setHeader('Access-Control-Allow-Origin', CONFIG.allowedOrigins);
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
}

View File

@@ -193,6 +193,7 @@ class StressTestingTool {
this.loadTheme();
this.loadSavedConfigs();
this.setupKeyboardShortcuts();
this.fetchGitInfo();
}
bindElements() {
@@ -261,6 +262,11 @@ class StressTestingTool {
themeToggle: document.getElementById("themeToggle"),
presetSelect: document.getElementById("presetSelect"),
saveConfigBtn: document.getElementById("saveConfigBtn"),
// Git Info
gitInfo: document.getElementById("gitInfo"),
gitCommit: document.getElementById("gitCommit"),
gitDate: document.getElementById("gitDate"),
};
}
@@ -452,6 +458,32 @@ class StressTestingTool {
location.reload(); // Reload to update preset list
}
async fetchGitInfo() {
try {
// Ensure we don't have double slashes if proxyUrl ends with slash (it shouldn't based on init logic)
const url = `${this.config.proxyUrl}/git-info`;
const response = await fetch(url);
if (response.ok) {
const data = await response.json();
if (data.commit && data.date && data.commit !== 'Unknown') {
if (this.elements.gitCommit) this.elements.gitCommit.textContent = data.commit;
if (this.elements.gitDate) {
let dateStr = data.date;
// Shorten to match screenshot style (approximate)
dateStr = dateStr.replace(/ days? ago/, 'd ago')
.replace(/ hours? ago/, 'h ago')
.replace(/ minutes? ago/, 'm ago')
.replace(/ seconds? ago/, 's ago');
this.elements.gitDate.textContent = dateStr;
}
if (this.elements.gitInfo) this.elements.gitInfo.style.display = 'flex';
}
}
} catch (e) {
console.error('Failed to fetch git info:', e);
}
}
initializeCharts() {
const isDark =
document.documentElement.getAttribute("data-theme") === "dark";

View File

@@ -4,7 +4,7 @@
=================================== */
/* Import Modern Typography */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap");
/* ===================================
CSS VARIABLES - DESIGN TOKENS
@@ -60,8 +60,9 @@
--radius-xl: 1rem;
/* Typography */
--font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'JetBrains Mono', 'Courier New', monospace;
--font-primary: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
--font-mono: "JetBrains Mono", "Courier New", monospace;
/* Transitions */
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
@@ -116,15 +117,22 @@ body {
/* Animated Background */
body::before {
content: '';
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 30%, rgba(99, 102, 241, 0.1) 0%, transparent 50%),
radial-gradient(circle at 80% 70%, rgba(139, 92, 246, 0.1) 0%, transparent 50%);
background: radial-gradient(
circle at 20% 30%,
rgba(99, 102, 241, 0.1) 0%,
transparent 50%
),
radial-gradient(
circle at 80% 70%,
rgba(139, 92, 246, 0.1) 0%,
transparent 50%
);
pointer-events: none;
z-index: 0;
}
@@ -201,7 +209,7 @@ body::before {
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-md);
background: linear-gradient(135deg, #FF0000 0%, #CC0000 100%);
background: linear-gradient(135deg, #ff0000 0%, #cc0000 100%);
color: white;
text-decoration: none;
border-radius: var(--radius-md);
@@ -227,7 +235,6 @@ body::before {
}
@keyframes pulse-glow {
0%,
100% {
box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3);
@@ -719,7 +726,7 @@ body::before {
}
.status-badge::before {
content: '';
content: "";
width: 8px;
height: 8px;
border-radius: 50%;
@@ -784,16 +791,18 @@ body::before {
}
.progress-bar::after {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg,
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.3),
transparent);
transparent
);
animation: shimmer 2s infinite;
}
@@ -887,7 +896,6 @@ body::before {
ANIMATIONS
=================================== */
@keyframes pulse {
0%,
100% {
opacity: 1;
@@ -1044,3 +1052,45 @@ body::before {
.full-width {
grid-column: 1 / -1;
}
/* ===================================
GIT INFO PILL
=================================== */
.git-info-pill {
position: fixed;
bottom: 20px;
right: 20px;
background-color: var(--color-bg-secondary);
border: 1px solid var(--color-border);
padding: 8px 16px;
border-radius: 9999px;
font-family: var(--font-mono);
font-size: 0.875rem;
color: var(--color-text-muted);
display: flex;
align-items: center;
gap: 8px;
box-shadow: var(--shadow-md);
z-index: 1000;
backdrop-filter: blur(10px);
transition: all var(--transition-base);
}
.git-info-pill:hover {
border-color: var(--color-accent-secondary);
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.git-commit {
color: var(--color-accent-secondary);
font-weight: 600;
}
.git-separator {
color: var(--color-text-tertiary);
}
.git-date {
color: var(--color-text-tertiary);
}