From d6cf8f6603ff991d389740562c2795849b6165fe Mon Sep 17 00:00:00 2001 From: DeNNiiInc Date: Sat, 27 Dec 2025 17:44:39 +1100 Subject: [PATCH] Feat: Add git commit info badge --- index.html | 370 +++++++++++++++++++++++++++------------------- public/script.js | 50 +++++++ public/styles.css | 294 +++++++++++++++++++++--------------- server.js | 20 +++ 4 files changed, 458 insertions(+), 276 deletions(-) diff --git a/index.html b/index.html index 1813d6e..aa5dc95 100644 --- a/index.html +++ b/index.html @@ -1,177 +1,239 @@ + + + + + Advanced SMTP Tester + + - - - - - Advanced SMTP Tester - - + + + + + - - - - - + + + - - - +
-
- -
- -
-
-

- Logo - SMTP Tester -

-

- Advanced SMTP Configuration & Delivery Testing Utility -

- - - - - Watch on YouTube @beyondcloudtechnology - -
-
- - -
-
-
-
📧
-

Server Configuration

+
+ +
+
+

+ Logo + SMTP Tester +

+

+ Advanced SMTP Configuration & Delivery Testing Utility +

+ + + + + Watch on YouTube @beyondcloudtechnology +
+
-
- -
- - + +
+
+
+
📧
+

Server Configuration

-
- -
- - -
-
- - -
-
- - + + + + + + +
- -
- - -
-
- -
- - +
+ +
+ + +
+
+ + +
+
+ + +
+ + +
+ + +
+
+ +
+ + +
+
+ + +
+ + +
+
+ +
- -
- - +
+ +
-
- - + + +
+ ⚠️ +

+ Note: Per your request, the password used for + this test will be included in the body of the test email. +

+
+ + + + + + +
+
+

📋 Test History

+ +
+
+
+ No test history yet. Run a test to see it here! +
- -
- - -
- - -
- ⚠️ -

- Note: Per your request, the password used for - this test will be included in the body of the test email. -

-
- - - - +
- -
-
-

📋 Test History

- -
-
-
No test history yet. Run a test to see it here!
-
-
-
-
+ + +
- - - + + - - - - - \ No newline at end of file + + + + diff --git a/public/script.js b/public/script.js index 1104243..1f863dc 100644 --- a/public/script.js +++ b/public/script.js @@ -469,6 +469,53 @@ document.getElementById('autoTestBtn').addEventListener('click', async function } }); + } +}); + +// =================================== +// VERSION INFO +// =================================== +async function loadVersionInfo() { + try { + const response = await fetch('/api/version'); + const data = await response.json(); + + if (data.hash && data.hash !== 'Unknown') { + const badge = document.getElementById('versionBadge'); + const hashSpan = badge.querySelector('.commit-hash'); + const dateSpan = badge.querySelector('.commit-date'); + + hashSpan.textContent = data.hash; // e.g., 9f3a914 + dateSpan.textContent = timeAgo(new Date(data.date)); // e.g., 3d ago + + badge.classList.remove('hidden'); + } + } catch (e) { + console.warn('Failed to load version info:', e); + } +} + +function timeAgo(date) { + const seconds = Math.floor((new Date() - date) / 1000); + + let interval = seconds / 31536000; + if (interval > 1) return Math.floor(interval) + "y ago"; + + interval = seconds / 2592000; + if (interval > 1) return Math.floor(interval) + "mo ago"; + + interval = seconds / 86400; + if (interval > 1) return Math.floor(interval) + "d ago"; + + interval = seconds / 3600; + if (interval > 1) return Math.floor(interval) + "h ago"; + + interval = seconds / 60; + if (interval > 1) return Math.floor(interval) + "m ago"; + + return Math.floor(seconds) + "s ago"; +} + // =================================== // INITIALIZATION // =================================== @@ -482,6 +529,9 @@ window.addEventListener('DOMContentLoaded', async () => { // Load history await loadHistory(); + // Load Version Info + await loadVersionInfo(); + // Load last used configuration const settings = await window.smtpDB.getSettings(); if (settings) { diff --git a/public/styles.css b/public/styles.css index c594eeb..6af94e0 100644 --- a/public/styles.css +++ b/public/styles.css @@ -77,12 +77,12 @@ body { left: 0; width: 100%; height: 100%; - background: radial-gradient(circle at 15% 50%, + background: radial-gradient( + circle at 15% 50%, var(--gradient-bg-1) 0%, - transparent 25%), - radial-gradient(circle at 85% 30%, - var(--gradient-bg-2) 0%, - transparent 25%); + transparent 25% + ), + radial-gradient(circle at 85% 30%, var(--gradient-bg-2) 0%, transparent 25%); z-index: -1; pointer-events: none; transition: background 0.3s ease; @@ -276,9 +276,11 @@ select:focus { } .btn-primary { - background: linear-gradient(135deg, - var(--accent-primary), - var(--accent-hover)); + background: linear-gradient( + 135deg, + var(--accent-primary), + var(--accent-hover) + ); color: white; box-shadow: 0 4px 12px var(--accent-glow); } @@ -449,217 +451,265 @@ select:focus { .github-icon { width: 18px; height: 18px; -}/* Theme Toggle Button */ +} /* Theme Toggle Button */ .theme-toggle { - position: fixed; - top: 1.5rem; - right: 1.5rem; - width: 50px; - height: 50px; - border-radius: 50%; - background: var(--bg-glass); - border: 1px solid var(--border-color); - color: var(--text-primary); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - transition: all 0.3s ease; - z-index: 1000; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + position: fixed; + top: 1.5rem; + right: 1.5rem; + width: 50px; + height: 50px; + border-radius: 50%; + background: var(--bg-glass); + border: 1px solid var(--border-color); + color: var(--text-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + transition: all 0.3s ease; + z-index: 1000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } .theme-toggle:hover { - transform: scale(1.1); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); + transform: scale(1.1); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); } .theme-toggle:active { - transform: scale(0.95); + transform: scale(0.95); } /* Preset Selector */ .preset-selector { - margin-bottom: var(--spacing-md); - padding: var(--spacing-md); - background: rgba(99, 102, 241, 0.05); - border: 1px solid rgba(99, 102, 241, 0.2); - border-radius: var(--radius-md); + margin-bottom: var(--spacing-md); + padding: var(--spacing-md); + background: rgba(99, 102, 241, 0.05); + border: 1px solid rgba(99, 102, 241, 0.2); + border-radius: var(--radius-md); } .preset-selector label { - color: var(--accent-primary); - font-weight: 600; - margin-bottom: 0.5rem; - display: block; + color: var(--accent-primary); + font-weight: 600; + margin-bottom: 0.5rem; + display: block; } .preset-selector select { - width: 100%; - background: var(--bg-input); - border: 1px solid var(--accent-primary); + width: 100%; + background: var(--bg-input); + border: 1px solid var(--accent-primary); } /* History Panel */ .history-panel { - margin-top: var(--spacing-lg); - padding: var(--spacing-md); - background: var(--bg-glass); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - max-height: 400px; - overflow-y: auto; + margin-top: var(--spacing-lg); + padding: var(--spacing-md); + background: var(--bg-glass); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + max-height: 400px; + overflow-y: auto; } .history-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-sm); - padding-bottom: var(--spacing-sm); - border-bottom: 1px solid var(--border-color); + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--spacing-sm); + padding-bottom: var(--spacing-sm); + border-bottom: 1px solid var(--border-color); } .history-header h3 { - font-size: 1.1rem; - color: var(--text-primary); + font-size: 1.1rem; + color: var(--text-primary); } .clear-history-btn { - padding: 0.4rem 0.8rem; - background: rgba(239, 68, 68, 0.1); - border: 1px solid var(--error); - border-radius: var(--radius-sm); - color: var(--error); - font-size: 0.85rem; - cursor: pointer; - transition: all 0.2s ease; + padding: 0.4rem 0.8rem; + background: rgba(239, 68, 68, 0.1); + border: 1px solid var(--error); + border-radius: var(--radius-sm); + color: var(--error); + font-size: 0.85rem; + cursor: pointer; + transition: all 0.2s ease; } .clear-history-btn:hover { - background: rgba(239, 68, 68, 0.2); + background: rgba(239, 68, 68, 0.2); } .history-item { - padding: var(--spacing-sm); - margin-bottom: var(--spacing-sm); - background: var(--bg-input); - border: 1px solid var(--border-color); - border-radius: var(--radius-sm); - cursor: pointer; - transition: all 0.2s ease; + padding: var(--spacing-sm); + margin-bottom: var(--spacing-sm); + background: var(--bg-input); + border: 1px solid var(--border-color); + border-radius: var(--radius-sm); + cursor: pointer; + transition: all 0.2s ease; } .history-item:hover { - border-color: var(--accent-primary); - transform: translateX(4px); + border-color: var(--accent-primary); + transform: translateX(4px); } .history-item-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5rem; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; } .history-item-host { - font-family: "JetBrains Mono", monospace; - font-size: 0.9rem; - color: var(--text-primary); - font-weight: 600; + font-family: "JetBrains Mono", monospace; + font-size: 0.9rem; + color: var(--text-primary); + font-weight: 600; } .history-item-time { - font-size: 0.75rem; - color: var(--text-secondary); + font-size: 0.75rem; + color: var(--text-secondary); } .history-item-details { - display: flex; - gap: 1rem; - font-size: 0.8rem; - color: var(--text-secondary); + display: flex; + gap: 1rem; + font-size: 0.8rem; + color: var(--text-secondary); } .history-item-status { - padding: 0.2rem 0.5rem; - border-radius: var(--radius-sm); - font-size: 0.75rem; - font-weight: 600; + padding: 0.2rem 0.5rem; + border-radius: var(--radius-sm); + font-size: 0.75rem; + font-weight: 600; } .history-item-status.success { - background: rgba(16, 185, 129, 0.1); - color: var(--success); + background: rgba(16, 185, 129, 0.1); + color: var(--success); } .history-item-status.error { - background: rgba(239, 68, 68, 0.1); - color: var(--error); + background: rgba(239, 68, 68, 0.1); + color: var(--error); } .history-empty { - text-align: center; - padding: var(--spacing-lg); - color: var(--text-secondary); - font-style: italic; + text-align: center; + padding: var(--spacing-lg); + color: var(--text-secondary); + font-style: italic; } /* Error Tips */ .error-tips { - margin-top: var(--spacing-sm); - padding: var(--spacing-sm); - background: rgba(239, 68, 68, 0.05); - border-left: 3px solid var(--error); - border-radius: var(--radius-sm); + margin-top: var(--spacing-sm); + padding: var(--spacing-sm); + background: rgba(239, 68, 68, 0.05); + border-left: 3px solid var(--error); + border-radius: var(--radius-sm); } .error-tips-title { - font-weight: 600; - color: var(--error); - margin-bottom: 0.5rem; - font-size: 0.9rem; + font-weight: 600; + color: var(--error); + margin-bottom: 0.5rem; + font-size: 0.9rem; } .error-tips ul { - margin-left: 1.2rem; - color: var(--text-secondary); - font-size: 0.85rem; + margin-left: 1.2rem; + color: var(--text-secondary); + font-size: 0.85rem; } .error-tips li { - margin-bottom: 0.3rem; + margin-bottom: 0.3rem; } .error-tips a { - color: var(--accent-primary); - text-decoration: none; + color: var(--accent-primary); + text-decoration: none; } .error-tips a:hover { - text-decoration: underline; + text-decoration: underline; } /* Scrollbar Styling */ .history-panel::-webkit-scrollbar, .log-output::-webkit-scrollbar { - width: 8px; + width: 8px; } .history-panel::-webkit-scrollbar-track, .log-output::-webkit-scrollbar-track { - background: var(--bg-input); - border-radius: var(--radius-sm); + background: var(--bg-input); + border-radius: var(--radius-sm); } .history-panel::-webkit-scrollbar-thumb, .log-output::-webkit-scrollbar-thumb { - background: var(--border-color); - border-radius: var(--radius-sm); + background: var(--border-color); + border-radius: var(--radius-sm); } .history-panel::-webkit-scrollbar-thumb:hover, .log-output::-webkit-scrollbar-thumb:hover { - background: var(--text-secondary); + background: var(--text-secondary); +} + +/* Version Badge */ +.version-badge { + position: fixed; + bottom: 1.5rem; + right: 1.5rem; + background: rgba(15, 23, 42, 0.8); + backdrop-filter: blur(8px); + border: 1px solid var(--border-color); + border-radius: 20px; + padding: 0.4rem 0.8rem; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + z-index: 1000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + transition: all 0.3s ease; +} + +[data-theme="light"] .version-badge { + background: rgba(255, 255, 255, 0.8); + border-color: rgba(0, 0, 0, 0.1); +} + +.version-badge:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); +} + +.commit-hash { + font-family: "JetBrains Mono", monospace; + color: #a78bfa; /* Slightly purple to match example */ + font-weight: 600; +} + +[data-theme="light"] .commit-hash { + color: #7c3aed; +} + +.commit-sep { + color: var(--text-secondary); + font-size: 0.6rem; +} + +.commit-date { + color: var(--text-secondary); } diff --git a/server.js b/server.js index 33318e7..9d8d556 100644 --- a/server.js +++ b/server.js @@ -3,10 +3,25 @@ const nodemailer = require("nodemailer"); const bodyParser = require("body-parser"); const cors = require("cors"); const path = require("path"); +const { execSync } = require("child_process"); const app = express(); const port = process.env.PORT || 3000; +// Get Git Commit Info +let gitInfo = { + hash: 'Unknown', + date: 'Unknown' +}; + +try { + const hash = execSync('git log -1 --format="%h"').toString().trim(); + const date = execSync('git log -1 --format="%cd"').toString().trim(); + gitInfo = { hash, date }; +} catch (e) { + console.warn("Could not retrieve git info:", e.message); +} + app.use(cors()); app.use(bodyParser.json()); @@ -18,6 +33,11 @@ app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); +// API Endpoint for Version Info +app.get('/api/version', (req, res) => { + res.json(gitInfo); +}); + app.post('/api/test-smtp', async (req, res) => { const { host, port, secure, user, pass, from, to } = req.body;