mirror of
https://github.com/DeNNiiInc/Advanced-Smtp-Tester.git
synced 2026-04-17 17:35:59 +00:00
feat: Phase 1 Quick Wins - Dark mode, presets, history, and enhanced errors
- Add dark/light theme toggle with IndexedDB persistence - Implement configuration presets (Office365, Gmail, SendGrid, Mailgun, Amazon SES) - Add test history panel with last 50 tests and click-to-load - Create IndexedDB wrapper for persistent storage - Add enhanced error messages with troubleshooting tips - Improve UX with auto-save last configuration - Add custom scrollbars and smooth theme transitions
This commit is contained in:
199
public/db.js
Normal file
199
public/db.js
Normal file
@@ -0,0 +1,199 @@
|
||||
// IndexedDB Helper for SMTP Tester
|
||||
// Manages persistent storage for test history, settings, and preferences
|
||||
|
||||
const DB_NAME = 'SMTPTesterDB';
|
||||
const DB_VERSION = 1;
|
||||
const STORES = {
|
||||
HISTORY: 'testHistory',
|
||||
SETTINGS: 'settings',
|
||||
PREFERENCES: 'preferences'
|
||||
};
|
||||
|
||||
class SMTPDatabase {
|
||||
constructor() {
|
||||
this.db = null;
|
||||
}
|
||||
|
||||
// Initialize database
|
||||
async init() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onsuccess = () => {
|
||||
this.db = request.result;
|
||||
resolve(this.db);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
|
||||
// Create test history store
|
||||
if (!db.objectStoreNames.contains(STORES.HISTORY)) {
|
||||
const historyStore = db.createObjectStore(STORES.HISTORY, {
|
||||
keyPath: 'id',
|
||||
autoIncrement: true
|
||||
});
|
||||
historyStore.createIndex('timestamp', 'timestamp', { unique: false });
|
||||
historyStore.createIndex('host', 'host', { unique: false });
|
||||
}
|
||||
|
||||
// Create settings store
|
||||
if (!db.objectStoreNames.contains(STORES.SETTINGS)) {
|
||||
db.createObjectStore(STORES.SETTINGS, { keyPath: 'key' });
|
||||
}
|
||||
|
||||
// Create preferences store
|
||||
if (!db.objectStoreNames.contains(STORES.PREFERENCES)) {
|
||||
db.createObjectStore(STORES.PREFERENCES, { keyPath: 'key' });
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Save test result to history
|
||||
async saveTest(testData) {
|
||||
const transaction = this.db.transaction([STORES.HISTORY], 'readwrite');
|
||||
const store = transaction.objectStore(STORES.HISTORY);
|
||||
|
||||
const record = {
|
||||
...testData,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.add(record);
|
||||
request.onsuccess = () => {
|
||||
this.cleanupOldHistory(); // Keep only last 50 records
|
||||
resolve(request.result);
|
||||
};
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Get test history (last N records)
|
||||
async getHistory(limit = 50) {
|
||||
const transaction = this.db.transaction([STORES.HISTORY], 'readonly');
|
||||
const store = transaction.objectStore(STORES.HISTORY);
|
||||
const index = store.index('timestamp');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = index.openCursor(null, 'prev'); // Reverse order (newest first)
|
||||
const results = [];
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (cursor && results.length < limit) {
|
||||
results.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(results);
|
||||
}
|
||||
};
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up old history (keep only last 50)
|
||||
async cleanupOldHistory() {
|
||||
const transaction = this.db.transaction([STORES.HISTORY], 'readwrite');
|
||||
const store = transaction.objectStore(STORES.HISTORY);
|
||||
const index = store.index('timestamp');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = index.openCursor(null, 'prev');
|
||||
let count = 0;
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
const cursor = event.target.result;
|
||||
if (cursor) {
|
||||
count++;
|
||||
if (count > 50) {
|
||||
cursor.delete();
|
||||
}
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Save user settings (last used configuration)
|
||||
async saveSettings(settings) {
|
||||
const transaction = this.db.transaction([STORES.SETTINGS], 'readwrite');
|
||||
const store = transaction.objectStore(STORES.SETTINGS);
|
||||
|
||||
const record = {
|
||||
key: 'lastConfig',
|
||||
...settings,
|
||||
updatedAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.put(record);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Get user settings
|
||||
async getSettings() {
|
||||
const transaction = this.db.transaction([STORES.SETTINGS], 'readonly');
|
||||
const store = transaction.objectStore(STORES.SETTINGS);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.get('lastConfig');
|
||||
request.onsuccess = () => resolve(request.result || null);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Save preference (e.g., theme)
|
||||
async savePreference(key, value) {
|
||||
const transaction = this.db.transaction([STORES.PREFERENCES], 'readwrite');
|
||||
const store = transaction.objectStore(STORES.PREFERENCES);
|
||||
|
||||
const record = { key, value };
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.put(record);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Get preference
|
||||
async getPreference(key) {
|
||||
const transaction = this.db.transaction([STORES.PREFERENCES], 'readonly');
|
||||
const store = transaction.objectStore(STORES.PREFERENCES);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.get(key);
|
||||
request.onsuccess = () => resolve(request.result ? request.result.value : null);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all history
|
||||
async clearHistory() {
|
||||
const transaction = this.db.transaction([STORES.HISTORY], 'readwrite');
|
||||
const store = transaction.objectStore(STORES.HISTORY);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = store.clear();
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
const db = new SMTPDatabase();
|
||||
|
||||
// Initialize on load
|
||||
db.init().catch(err => console.error('Failed to initialize database:', err));
|
||||
|
||||
// Export for use in other scripts
|
||||
window.smtpDB = db;
|
||||
Reference in New Issue
Block a user