Add comprehensive database setup and user management system

- Implement PostgreSQL database schema with users and bookmarks tables
- Add database connection pooling with retry logic and error handling
- Create migration system with automatic schema initialization
- Add database CLI tools for management (init, status, validate, etc.)
- Include comprehensive error handling and diagnostics
- Add development seed data and testing utilities
- Implement health monitoring and connection pool statistics
- Create detailed documentation and troubleshooting guide

Database features:
- Users table with authentication fields and email verification
- Bookmarks table with user association and metadata
- Proper indexes for performance optimization
- Automatic timestamp triggers
- Transaction support with rollback handling
- Connection pooling (20 max connections, 30s idle timeout)
- Graceful shutdown handling

CLI commands available:
- npm run db:init - Initialize database
- npm run db:status - Check database status
- npm run db:validate - Validate schema
- npm run db:test - Run database tests
- npm run db:diagnostics - Full diagnostics
This commit is contained in:
2025-07-19 23:21:50 +02:00
commit 0abee5b794
66 changed files with 45023 additions and 0 deletions

View File

@ -0,0 +1,299 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Import Test - Bookmark Manager</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>Advanced Import/Export Features Test</h1>
<div class="test-section">
<h2>Test Chrome Bookmarks JSON</h2>
<p>Test the Chrome bookmarks JSON parser with sample data:</p>
<button id="testChromeParser" class="btn btn-primary">Test Chrome Parser</button>
<div id="chromeTestResult" class="test-result"></div>
</div>
<div class="test-section">
<h2>Test Firefox Bookmarks JSON</h2>
<p>Test the Firefox bookmarks JSON parser with sample data:</p>
<button id="testFirefoxParser" class="btn btn-primary">Test Firefox Parser</button>
<div id="firefoxTestResult" class="test-result"></div>
</div>
<div class="test-section">
<h2>Test Import Preview</h2>
<p>Test the import preview functionality:</p>
<button id="testImportPreview" class="btn btn-primary">Test Import Preview</button>
<div id="previewTestResult" class="test-result"></div>
</div>
<div class="test-section">
<h2>Test Duplicate Detection</h2>
<p>Test the enhanced duplicate detection algorithms:</p>
<button id="testDuplicateDetection" class="btn btn-primary">Test Duplicate Detection</button>
<div id="duplicateTestResult" class="test-result"></div>
</div>
<div class="test-section">
<h2>Test Sync Functionality</h2>
<p>Test the device synchronization features:</p>
<button id="testSyncFeatures" class="btn btn-primary">Test Sync Features</button>
<div id="syncTestResult" class="test-result"></div>
</div>
</div>
<script src="script.js"></script>
<script>
// Initialize bookmark manager
const bookmarkManager = new BookmarkManager();
// Test Chrome parser
document.getElementById('testChromeParser').addEventListener('click', () => {
const sampleChromeData = {
"roots": {
"bookmark_bar": {
"children": [
{
"type": "url",
"name": "Google",
"url": "https://www.google.com",
"date_added": "13285166270000000"
},
{
"type": "folder",
"name": "Development",
"children": [
{
"type": "url",
"name": "GitHub",
"url": "https://github.com",
"date_added": "13285166280000000"
}
]
}
]
}
}
};
try {
const bookmarks = bookmarkManager.parseChromeBookmarks(JSON.stringify(sampleChromeData));
document.getElementById('chromeTestResult').innerHTML = `
<div class="test-success">✅ Chrome parser test passed!</div>
<div>Parsed ${bookmarks.length} bookmarks:</div>
<ul>
${bookmarks.map(b => `<li>${b.title} - ${b.url} (${b.folder || 'Root'})</li>`).join('')}
</ul>
`;
} catch (error) {
document.getElementById('chromeTestResult').innerHTML = `
<div class="test-error">❌ Chrome parser test failed: ${error.message}</div>
`;
}
});
// Test Firefox parser
document.getElementById('testFirefoxParser').addEventListener('click', () => {
const sampleFirefoxData = [
{
"type": "text/x-moz-place-container",
"title": "Bookmarks Menu",
"children": [
{
"type": "text/x-moz-place",
"title": "Mozilla Firefox",
"uri": "https://www.mozilla.org/firefox/",
"dateAdded": 1642534567890000
},
{
"type": "text/x-moz-place-container",
"title": "Development",
"children": [
{
"type": "text/x-moz-place",
"title": "MDN Web Docs",
"uri": "https://developer.mozilla.org/",
"dateAdded": 1642534577890000
}
]
}
]
}
];
try {
const bookmarks = bookmarkManager.parseFirefoxBookmarks(JSON.stringify(sampleFirefoxData));
document.getElementById('firefoxTestResult').innerHTML = `
<div class="test-success">✅ Firefox parser test passed!</div>
<div>Parsed ${bookmarks.length} bookmarks:</div>
<ul>
${bookmarks.map(b => `<li>${b.title} - ${b.url} (${b.folder || 'Root'})</li>`).join('')}
</ul>
`;
} catch (error) {
document.getElementById('firefoxTestResult').innerHTML = `
<div class="test-error">❌ Firefox parser test failed: ${error.message}</div>
`;
}
});
// Test import preview
document.getElementById('testImportPreview').addEventListener('click', () => {
const testBookmarks = [
{
id: 'test1',
title: 'Test Bookmark 1',
url: 'https://example.com',
folder: 'Test Folder',
addDate: Date.now(),
status: 'unknown'
},
{
id: 'test2',
title: 'Test Bookmark 2',
url: 'https://test.com',
folder: 'Another Folder',
addDate: Date.now(),
status: 'unknown'
}
];
const importData = {
bookmarks: testBookmarks,
format: 'test',
originalCount: testBookmarks.length
};
try {
const analysis = bookmarkManager.analyzeImportData(importData, 'merge');
document.getElementById('previewTestResult').innerHTML = `
<div class="test-success">✅ Import preview test passed!</div>
<div>Analysis results:</div>
<ul>
<li>New bookmarks: ${analysis.newBookmarks.length}</li>
<li>Duplicates: ${analysis.duplicates.length}</li>
<li>Folders: ${analysis.folders.length}</li>
</ul>
`;
} catch (error) {
document.getElementById('previewTestResult').innerHTML = `
<div class="test-error">❌ Import preview test failed: ${error.message}</div>
`;
}
});
// Test duplicate detection
document.getElementById('testDuplicateDetection').addEventListener('click', () => {
// Add some test bookmarks first
bookmarkManager.bookmarks = [
{
id: 'existing1',
title: 'Google Search',
url: 'https://www.google.com/',
folder: 'Search',
addDate: Date.now() - 1000000,
status: 'valid'
}
];
const testBookmark = {
title: 'Google',
url: 'https://google.com',
folder: 'Web',
addDate: Date.now(),
status: 'unknown'
};
try {
const duplicate = bookmarkManager.findDuplicateBookmark(testBookmark, true, false);
const similarity = bookmarkManager.calculateStringSimilarity('Google Search', 'Google');
document.getElementById('duplicateTestResult').innerHTML = `
<div class="test-success">✅ Duplicate detection test passed!</div>
<div>Test results:</div>
<ul>
<li>Duplicate found: ${duplicate ? 'Yes' : 'No'}</li>
<li>Title similarity: ${Math.round(similarity * 100)}%</li>
<li>URL normalization working: ${bookmarkManager.normalizeUrl('https://www.google.com/') === bookmarkManager.normalizeUrl('https://google.com')}</li>
</ul>
`;
} catch (error) {
document.getElementById('duplicateTestResult').innerHTML = `
<div class="test-error">❌ Duplicate detection test failed: ${error.message}</div>
`;
}
});
// Test sync functionality
document.getElementById('testSyncFeatures').addEventListener('click', () => {
try {
const deviceId = bookmarkManager.getDeviceId();
const syncData = bookmarkManager.exportForSync();
const dataHash = bookmarkManager.calculateDataHash();
document.getElementById('syncTestResult').innerHTML = `
<div class="test-success">✅ Sync features test passed!</div>
<div>Test results:</div>
<ul>
<li>Device ID: ${deviceId}</li>
<li>Sync data generated: ${syncData.bookmarks.length} bookmarks</li>
<li>Data hash: ${dataHash}</li>
<li>Compression working: ${bookmarkManager.compressAndEncodeData({test: 'data'}).length > 0}</li>
</ul>
`;
} catch (error) {
document.getElementById('syncTestResult').innerHTML = `
<div class="test-error">❌ Sync features test failed: ${error.message}</div>
`;
}
});
</script>
<style>
.test-section {
margin: 20px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #f9f9f9;
}
.test-result {
margin-top: 15px;
padding: 10px;
border-radius: 4px;
}
.test-success {
color: #155724;
background-color: #d4edda;
border: 1px solid #c3e6cb;
padding: 8px 12px;
border-radius: 4px;
margin-bottom: 10px;
}
.test-error {
color: #721c24;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
padding: 8px 12px;
border-radius: 4px;
margin-bottom: 10px;
}
.test-result ul {
margin: 10px 0;
padding-left: 20px;
}
.test-result li {
margin: 5px 0;
}
</style>
</body>
</html>