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,342 @@
// Test individual functions for advanced import functionality
// Test Chrome bookmarks parsing
function testChromeParser() {
console.log('Testing Chrome bookmarks parser...');
const sampleData = {
"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"
}
]
}
]
}
}
};
function parseChromeBookmarks(content) {
const data = JSON.parse(content);
const bookmarks = [];
const parseFolder = (folder, parentPath = '') => {
if (!folder.children) return;
folder.children.forEach(item => {
if (item.type === 'url') {
bookmarks.push({
id: Date.now() + Math.random() + bookmarks.length,
title: item.name || 'Untitled',
url: item.url,
folder: parentPath,
addDate: item.date_added ? parseInt(item.date_added) / 1000 : Date.now(),
icon: '',
status: 'unknown'
});
} else if (item.type === 'folder') {
const folderPath = parentPath ? `${parentPath} / ${item.name}` : item.name;
parseFolder(item, folderPath);
}
});
};
if (data.roots) {
if (data.roots.bookmark_bar) {
parseFolder(data.roots.bookmark_bar, '');
}
}
return bookmarks;
}
try {
const bookmarks = parseChromeBookmarks(JSON.stringify(sampleData));
console.log(`✅ Chrome parser: Found ${bookmarks.length} bookmarks`);
bookmarks.forEach(b => console.log(` - ${b.title} (${b.folder || 'Root'})`));
return true;
} catch (error) {
console.log(`❌ Chrome parser failed: ${error.message}`);
return false;
}
}
// Test Firefox bookmarks parsing
function testFirefoxParser() {
console.log('\nTesting Firefox bookmarks parser...');
const sampleData = [
{
"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
}
]
}
]
}
];
function parseFirefoxBookmarks(content) {
const data = JSON.parse(content);
const bookmarks = [];
const parseItem = (item, parentPath = '') => {
if (item.type === 'text/x-moz-place') {
if (item.uri) {
bookmarks.push({
id: Date.now() + Math.random() + bookmarks.length,
title: item.title || 'Untitled',
url: item.uri,
folder: parentPath,
addDate: item.dateAdded ? item.dateAdded / 1000 : Date.now(),
icon: '',
status: 'unknown'
});
}
} else if (item.type === 'text/x-moz-place-container' && item.children) {
const folderPath = parentPath ? `${parentPath} / ${item.title}` : item.title;
item.children.forEach(child => parseItem(child, folderPath));
}
};
if (Array.isArray(data)) {
data.forEach(item => parseItem(item));
} else {
parseItem(data);
}
return bookmarks;
}
try {
const bookmarks = parseFirefoxBookmarks(JSON.stringify(sampleData));
console.log(`✅ Firefox parser: Found ${bookmarks.length} bookmarks`);
bookmarks.forEach(b => console.log(` - ${b.title} (${b.folder || 'Root'})`));
return true;
} catch (error) {
console.log(`❌ Firefox parser failed: ${error.message}`);
return false;
}
}
// Test format detection
function testFormatDetection() {
console.log('\nTesting format detection...');
function detectFileFormat(file, content) {
const fileName = file.name.toLowerCase();
if (fileName.endsWith('.json')) {
try {
const data = JSON.parse(content);
if (data.roots && data.roots.bookmark_bar) {
return 'chrome';
} else if (Array.isArray(data) && data[0] && data[0].type) {
return 'firefox';
}
} catch (e) {
// Not valid JSON
}
} else if (fileName.endsWith('.html') || fileName.endsWith('.htm')) {
if (content.includes('<!DOCTYPE NETSCAPE-Bookmark-file-1>')) {
return 'netscape';
}
}
return 'netscape';
}
try {
const chromeTest = detectFileFormat(
{ name: 'bookmarks.json' },
JSON.stringify({ roots: { bookmark_bar: {} } })
);
const firefoxTest = detectFileFormat(
{ name: 'bookmarks.json' },
JSON.stringify([{ type: 'text/x-moz-place-container' }])
);
const htmlTest = detectFileFormat(
{ name: 'bookmarks.html' },
'<!DOCTYPE NETSCAPE-Bookmark-file-1>'
);
console.log(`✅ Format detection working:`);
console.log(` - Chrome: ${chromeTest === 'chrome' ? '✅' : '❌'} (${chromeTest})`);
console.log(` - Firefox: ${firefoxTest === 'firefox' ? '✅' : '❌'} (${firefoxTest})`);
console.log(` - HTML: ${htmlTest === 'netscape' ? '✅' : '❌'} (${htmlTest})`);
return true;
} catch (error) {
console.log(`❌ Format detection failed: ${error.message}`);
return false;
}
}
// Test duplicate detection
function testDuplicateDetection() {
console.log('\nTesting duplicate detection...');
function normalizeUrl(url) {
try {
let normalized = url.toLowerCase().trim();
normalized = normalized.replace(/^https?:\/\/(www\.)?/, 'https://');
normalized = normalized.replace(/\/$/, '');
return normalized;
} catch (error) {
return url;
}
}
function calculateStringSimilarity(str1, str2) {
const len1 = str1.length;
const len2 = str2.length;
const matrix = Array(len2 + 1).fill().map(() => Array(len1 + 1).fill(0));
for (let i = 0; i <= len1; i++) matrix[0][i] = i;
for (let j = 0; j <= len2; j++) matrix[j][0] = j;
for (let j = 1; j <= len2; j++) {
for (let i = 1; i <= len1; i++) {
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
matrix[j][i] = Math.min(
matrix[j - 1][i] + 1,
matrix[j][i - 1] + 1,
matrix[j - 1][i - 1] + cost
);
}
}
const maxLen = Math.max(len1, len2);
return maxLen === 0 ? 1 : (maxLen - matrix[len2][len1]) / maxLen;
}
try {
const url1 = 'https://www.google.com/';
const url2 = 'https://google.com';
const normalized1 = normalizeUrl(url1);
const normalized2 = normalizeUrl(url2);
const title1 = 'Google Search Engine';
const title2 = 'Google';
const similarity = calculateStringSimilarity(title1, title2);
console.log(`✅ Duplicate detection working:`);
console.log(` - URL normalization: ${normalized1 === normalized2 ? '✅' : '❌'}`);
console.log(` - Original URLs: "${url1}" vs "${url2}"`);
console.log(` - Normalized: "${normalized1}" vs "${normalized2}"`);
console.log(` - Title similarity: ${Math.round(similarity * 100)}% ("${title1}" vs "${title2}")`);
return true;
} catch (error) {
console.log(`❌ Duplicate detection failed: ${error.message}`);
return false;
}
}
// Test sync functionality
function testSyncFunctionality() {
console.log('\nTesting sync functionality...');
function getDeviceId() {
return 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
function compressAndEncodeData(data) {
const jsonString = JSON.stringify(data);
return Buffer.from(jsonString).toString('base64');
}
function decodeAndDecompressData(encodedData) {
try {
const jsonString = Buffer.from(encodedData, 'base64').toString();
return JSON.parse(jsonString);
} catch (error) {
throw new Error('Invalid sync data format');
}
}
function calculateDataHash(bookmarks = []) {
const dataString = JSON.stringify(bookmarks.map(b => ({
id: b.id,
title: b.title,
url: b.url,
folder: b.folder
})));
let hash = 0;
for (let i = 0; i < dataString.length; i++) {
const char = dataString.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash.toString();
}
try {
const deviceId = getDeviceId();
const testData = { test: 'data', bookmarks: [] };
const compressed = compressAndEncodeData(testData);
const decompressed = decodeAndDecompressData(compressed);
const hash = calculateDataHash([]);
console.log(`✅ Sync functionality working:`);
console.log(` - Device ID: ${deviceId.startsWith('device_') ? '✅' : '❌'} (${deviceId})`);
console.log(` - Compression: ${decompressed.test === 'data' ? '✅' : '❌'}`);
console.log(` - Data hash: ${typeof hash === 'string' ? '✅' : '❌'} (${hash})`);
return true;
} catch (error) {
console.log(`❌ Sync functionality failed: ${error.message}`);
return false;
}
}
// Run all tests
console.log('🧪 Testing Advanced Import/Export Features\n');
const results = [
testChromeParser(),
testFirefoxParser(),
testFormatDetection(),
testDuplicateDetection(),
testSyncFunctionality()
];
const passed = results.filter(r => r).length;
const total = results.length;
console.log(`\n🎉 Test Results: ${passed}/${total} tests passed`);
if (passed === total) {
console.log('✅ All advanced import/export features are working correctly!');
} else {
console.log('❌ Some tests failed. Please check the implementation.');
}