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,270 @@
// Verification script for advanced import/export functionality
// This script tests the new features without requiring a browser
// Mock DOM elements and browser APIs
global.document = {
getElementById: (id) => ({
value: '',
checked: true,
style: { display: 'none' },
innerHTML: '',
textContent: '',
addEventListener: () => {},
classList: { add: () => {}, remove: () => {} },
setAttribute: () => {},
getAttribute: () => null
}),
querySelectorAll: () => [],
querySelector: () => null,
createElement: () => ({
className: '',
innerHTML: '',
appendChild: () => {},
addEventListener: () => {}
})
};
global.window = {
open: () => {},
addEventListener: () => {}
};
global.localStorage = {
getItem: () => null,
setItem: () => {},
removeItem: () => {}
};
global.alert = console.log;
global.confirm = () => true;
global.prompt = () => 'test';
// Mock FileReader
global.FileReader = class {
readAsText() {
setTimeout(() => {
this.onload({ target: { result: '{"test": "data"}' } });
}, 10);
}
};
// Mock DOMParser
global.DOMParser = class {
parseFromString(content, type) {
return {
querySelectorAll: () => [],
querySelector: () => null
};
}
};
global.btoa = (str) => Buffer.from(str).toString('base64');
global.atob = (str) => Buffer.from(str, 'base64').toString();
// Load the BookmarkManager class
const fs = require('fs');
const scriptContent = fs.readFileSync('script.js', 'utf8');
// Extract just the BookmarkManager class definition
const classMatch = scriptContent.match(/class BookmarkManager \{[\s\S]*?\n\}/);
if (!classMatch) {
console.error('Could not extract BookmarkManager class');
process.exit(1);
}
// Evaluate the class
eval(classMatch[0]);
// Test the advanced import functionality
async function testAdvancedImport() {
console.log('🧪 Testing Advanced Import/Export Features...\n');
const manager = new BookmarkManager();
// Test 1: Chrome bookmarks parsing
console.log('1⃣ Testing Chrome bookmarks parsing...');
try {
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"
}
]
}
]
}
}
};
const bookmarks = manager.parseChromeBookmarks(JSON.stringify(sampleChromeData));
console.log(` ✅ Parsed ${bookmarks.length} bookmarks from Chrome format`);
console.log(` 📁 Folders: ${[...new Set(bookmarks.map(b => b.folder || 'Root'))].join(', ')}`);
} catch (error) {
console.log(` ❌ Chrome parsing failed: ${error.message}`);
}
// Test 2: Firefox bookmarks parsing
console.log('\n2⃣ Testing Firefox bookmarks parsing...');
try {
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
}
]
}
]
}
];
const bookmarks = manager.parseFirefoxBookmarks(JSON.stringify(sampleFirefoxData));
console.log(` ✅ Parsed ${bookmarks.length} bookmarks from Firefox format`);
console.log(` 📁 Folders: ${[...new Set(bookmarks.map(b => b.folder || 'Root'))].join(', ')}`);
} catch (error) {
console.log(` ❌ Firefox parsing failed: ${error.message}`);
}
// Test 3: Format detection
console.log('\n3⃣ Testing format detection...');
try {
const chromeFormat = manager.detectFileFormat(
{ name: 'bookmarks.json' },
JSON.stringify({ roots: { bookmark_bar: {} } })
);
const firefoxFormat = manager.detectFileFormat(
{ name: 'bookmarks.json' },
JSON.stringify([{ type: 'text/x-moz-place-container' }])
);
const htmlFormat = manager.detectFileFormat(
{ name: 'bookmarks.html' },
'<!DOCTYPE NETSCAPE-Bookmark-file-1>'
);
console.log(` ✅ Chrome format detected: ${chromeFormat === 'chrome'}`);
console.log(` ✅ Firefox format detected: ${firefoxFormat === 'firefox'}`);
console.log(` ✅ HTML format detected: ${htmlFormat === 'netscape'}`);
} catch (error) {
console.log(` ❌ Format detection failed: ${error.message}`);
}
// Test 4: Duplicate detection
console.log('\n4⃣ Testing enhanced duplicate detection...');
try {
// Add some existing bookmarks
manager.bookmarks = [
{
id: 'existing1',
title: 'Google Search Engine',
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'
};
const duplicate = manager.findDuplicateBookmark(testBookmark, true, true);
const similarity = manager.calculateStringSimilarity('Google Search Engine', 'Google');
console.log(` ✅ Duplicate detection working: ${duplicate ? 'Found duplicate' : 'No duplicate'}`);
console.log(` 📊 Title similarity: ${Math.round(similarity * 100)}%`);
console.log(` 🔗 URL normalization: ${manager.normalizeUrl('https://www.google.com/') === manager.normalizeUrl('https://google.com')}`);
} catch (error) {
console.log(` ❌ Duplicate detection failed: ${error.message}`);
}
// Test 5: Import analysis
console.log('\n5⃣ Testing import analysis...');
try {
const importData = {
bookmarks: [
{
id: 'import1',
title: 'New Site',
url: 'https://example.com',
folder: 'Examples',
addDate: Date.now(),
status: 'unknown'
},
{
id: 'import2',
title: 'Google Clone',
url: 'https://google.com',
folder: 'Search',
addDate: Date.now(),
status: 'unknown'
}
],
format: 'test',
originalCount: 2
};
const analysis = manager.analyzeImportData(importData, 'merge');
console.log(` ✅ Analysis completed:`);
console.log(` 📊 New bookmarks: ${analysis.newBookmarks.length}`);
console.log(` 🔄 Duplicates: ${analysis.duplicates.length}`);
console.log(` 📁 Folders: ${analysis.folders.length}`);
} catch (error) {
console.log(` ❌ Import analysis failed: ${error.message}`);
}
// Test 6: Sync functionality
console.log('\n6⃣ Testing sync functionality...');
try {
const deviceId = manager.getDeviceId();
const syncData = manager.exportForSync();
const dataHash = manager.calculateDataHash();
const compressed = manager.compressAndEncodeData({ test: 'data' });
const decompressed = manager.decodeAndDecompressData(compressed);
console.log(` ✅ Device ID generated: ${deviceId.startsWith('device_')}`);
console.log(` 📦 Sync export working: ${syncData.bookmarks.length >= 0}`);
console.log(` 🔢 Data hash generated: ${typeof dataHash === 'string'}`);
console.log(` 🗜️ Compression working: ${decompressed.test === 'data'}`);
} catch (error) {
console.log(` ❌ Sync functionality failed: ${error.message}`);
}
console.log('\n🎉 Advanced Import/Export Features Testing Complete!');
}
// Run the tests
testAdvancedImport().catch(console.error);