97 lines
4 KiB
JavaScript
97 lines
4 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
const searchInput = document.getElementById('search-input');
|
|
const searchForm = document.getElementById('search-form');
|
|
|
|
if (searchInput && searchForm) {
|
|
const performSearch = async (e) => {
|
|
if (e) e.preventDefault();
|
|
const query = searchInput.value.trim();
|
|
if (!query) return;
|
|
|
|
const qLower = query.toLowerCase();
|
|
let publicNotes = [];
|
|
let privateNotes = [];
|
|
|
|
// 1. Search Public Notes (Backend)
|
|
try {
|
|
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
|
|
if (res.ok) {
|
|
publicNotes = await res.json();
|
|
} else {
|
|
console.error("Search API failed:", res.status);
|
|
}
|
|
} catch (e) {
|
|
console.error("Search error:", e);
|
|
}
|
|
|
|
// 2. Search Private Notes (Client-Side Decryption for Zero-Knowledge Privacy)
|
|
if (window.SpotNotes && window.SpotNotes.isLoggedIn) {
|
|
try {
|
|
const mineRes = await fetch('/api/notes/mine');
|
|
if (mineRes.ok) {
|
|
const myRawNotes = await mineRes.json();
|
|
for (const note of myRawNotes) {
|
|
let decryptedNote = note;
|
|
if (note.is_encrypted && window.SpotCrypto) {
|
|
let padKey = window.SpotCrypto.getCachedPadKey(note.notepad_id);
|
|
if (!padKey && note.notepad && note.notepad.encrypted_key) {
|
|
padKey = await window.SpotCrypto.unwrapNotepadKey(note.notepad_id, note.notepad.encrypted_key);
|
|
}
|
|
decryptedNote = await window.SpotCrypto.decryptNote(note, padKey);
|
|
}
|
|
|
|
const titleMatch = (decryptedNote.title || '').toLowerCase().includes(qLower);
|
|
const contentMatch = (decryptedNote.content || '').toLowerCase().includes(qLower);
|
|
|
|
if (titleMatch || contentMatch) {
|
|
privateNotes.push(decryptedNote);
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.warn("Error searching private notes locally:", e);
|
|
}
|
|
}
|
|
|
|
// Combine unique notes
|
|
const seenIds = new Set();
|
|
const combinedNotes = [];
|
|
[...privateNotes, ...publicNotes].forEach(note => {
|
|
if (!seenIds.has(note.id)) {
|
|
seenIds.add(note.id);
|
|
combinedNotes.push(note);
|
|
}
|
|
});
|
|
|
|
// If notes found, display them
|
|
if (combinedNotes.length > 0) {
|
|
if (window.SpotNotes && window.SpotNotes.displaySearchResults) {
|
|
window.SpotNotes.displaySearchResults(combinedNotes, null, query);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 3. Fallback: Search Location (Nominatim) if no notes found
|
|
let location = null;
|
|
try {
|
|
const nomRes = await fetch(`/api/geocode?q=${encodeURIComponent(query)}`);
|
|
const nomData = await nomRes.json();
|
|
if (nomData && nomData.length > 0) {
|
|
location = {
|
|
lat: parseFloat(nomData[0].lat),
|
|
lng: parseFloat(nomData[0].lon)
|
|
};
|
|
}
|
|
} catch (e) {
|
|
console.error("Geocoding error:", e);
|
|
}
|
|
|
|
// 4. Update UI
|
|
if (window.SpotNotes && window.SpotNotes.displaySearchResults) {
|
|
window.SpotNotes.displaySearchResults(combinedNotes, location, query);
|
|
}
|
|
};
|
|
|
|
searchForm.addEventListener('submit', performSearch);
|
|
}
|
|
});
|