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

197
debug_favicons.html Normal file
View File

@ -0,0 +1,197 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Favicon Debug Tool</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.bookmark-debug {
display: flex;
align-items: center;
gap: 15px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 10px;
}
.favicon-test {
width: 16px;
height: 16px;
border: 1px solid #ccc;
flex-shrink: 0;
}
.bookmark-info {
flex: 1;
}
.bookmark-title {
font-weight: bold;
margin-bottom: 5px;
}
.favicon-data {
font-size: 12px;
color: #666;
word-break: break-all;
}
.status {
padding: 2px 6px;
border-radius: 3px;
font-size: 11px;
font-weight: bold;
}
.status.has-icon {
background: #d4edda;
color: #155724;
}
.status.no-icon {
background: #f8d7da;
color: #721c24;
}
.status.error {
background: #fff3cd;
color: #856404;
}
.summary {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
button {
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-bottom: 20px;
}
button:hover {
background: #0056b3;
}
</style>
</head>
<body>
<h1>Favicon Debug Tool</h1>
<p>This tool will analyze the favicon data in your bookmarks to help identify why some favicons aren't showing.</p>
<button onclick="analyzeBookmarks()">Analyze Bookmarks</button>
<div id="summary" class="summary" style="display: none;">
<h3>Summary</h3>
<div id="summaryContent"></div>
</div>
<div id="results"></div>
<script>
function analyzeBookmarks() {
const stored = localStorage.getItem('bookmarks');
const resultsDiv = document.getElementById('results');
const summaryDiv = document.getElementById('summary');
const summaryContent = document.getElementById('summaryContent');
if (!stored) {
resultsDiv.innerHTML = '<p>No bookmarks found in localStorage.</p>';
return;
}
let bookmarks;
try {
bookmarks = JSON.parse(stored);
} catch (error) {
resultsDiv.innerHTML = '<p>Error parsing bookmark data: ' + error.message + '</p>';
return;
}
let hasIcon = 0;
let noIcon = 0;
let errorIcon = 0;
let dataUrls = 0;
let httpUrls = 0;
resultsDiv.innerHTML = '';
bookmarks.forEach((bookmark, index) => {
const debugDiv = document.createElement('div');
debugDiv.className = 'bookmark-debug';
const favicon = document.createElement('img');
favicon.className = 'favicon-test';
favicon.alt = 'Favicon';
const infoDiv = document.createElement('div');
infoDiv.className = 'bookmark-info';
const titleDiv = document.createElement('div');
titleDiv.className = 'bookmark-title';
titleDiv.textContent = bookmark.title || 'Untitled';
const statusSpan = document.createElement('span');
statusSpan.className = 'status';
const dataDiv = document.createElement('div');
dataDiv.className = 'favicon-data';
if (bookmark.icon && bookmark.icon.trim() !== '') {
hasIcon++;
statusSpan.textContent = 'HAS ICON';
statusSpan.classList.add('has-icon');
if (bookmark.icon.startsWith('data:')) {
dataUrls++;
dataDiv.textContent = `Data URL (${bookmark.icon.length} chars): ${bookmark.icon.substring(0, 100)}...`;
} else {
httpUrls++;
dataDiv.textContent = `URL: ${bookmark.icon}`;
}
favicon.src = bookmark.icon;
favicon.onerror = function() {
statusSpan.textContent = 'ICON ERROR';
statusSpan.className = 'status error';
errorIcon++;
hasIcon--;
dataDiv.textContent += ' [FAILED TO LOAD]';
};
} else {
noIcon++;
statusSpan.textContent = 'NO ICON';
statusSpan.classList.add('no-icon');
dataDiv.textContent = 'No favicon data found';
// Use default icon
favicon.src = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZmlsbD0iIzk5OSIgZD0iTTggMEMzLjYgMCAwIDMuNiAwIDhzMy42IDggOCA4IDgtMy42IDgtOC0zLjYtOC04LTh6bTAgMTRjLTMuMyAwLTYtMi3LTYtNnMyLjctNiA2LTYgNiAyLjcgNiA2LTIuNyA2LTYgNnoiLz48L3N2Zz4=';
}
infoDiv.appendChild(titleDiv);
infoDiv.appendChild(statusSpan);
infoDiv.appendChild(dataDiv);
debugDiv.appendChild(favicon);
debugDiv.appendChild(infoDiv);
resultsDiv.appendChild(debugDiv);
});
// Show summary
summaryContent.innerHTML = `
<strong>Total Bookmarks:</strong> ${bookmarks.length}<br>
<strong>With Favicons:</strong> ${hasIcon} (${Math.round(hasIcon/bookmarks.length*100)}%)<br>
<strong>Without Favicons:</strong> ${noIcon} (${Math.round(noIcon/bookmarks.length*100)}%)<br>
<strong>Failed to Load:</strong> ${errorIcon} (${Math.round(errorIcon/bookmarks.length*100)}%)<br>
<strong>Data URLs:</strong> ${dataUrls}<br>
<strong>HTTP URLs:</strong> ${httpUrls}
`;
summaryDiv.style.display = 'block';
}
</script>
</body>
</html>