diff --git a/internal/shared/middleware/auth.go b/internal/shared/middleware/auth.go index 9b7c5ad..567d25d 100644 --- a/internal/shared/middleware/auth.go +++ b/internal/shared/middleware/auth.go @@ -61,8 +61,10 @@ func RequireAuth(next http.Handler) http.Handler { // RequireGlobalAuth ensures that a valid user is in the context for all routes except login and static func RequireGlobalAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Allow unauthenticated access to login and static files - if r.URL.Path == "/login" || strings.HasPrefix(r.URL.Path, "/static/") { + // Allow unauthenticated access to login, search, and static files + if r.URL.Path == "/login" || strings.HasPrefix(r.URL.Path, "/static/") || + r.URL.Path == "/search" || r.URL.Path == "/api/search" || r.URL.Path == "/api/search-quick" || + r.URL.Path == "/" { next.ServeHTTP(w, r) return } diff --git a/static/js/search.js b/static/js/search.js index 61636bd..e99e801 100644 --- a/static/js/search.js +++ b/static/js/search.js @@ -1,62 +1,67 @@ -let searchTimeout; -const searchInput = document.getElementById('search-input'); -const searchDropdown = document.getElementById('search-dropdown'); +(function() { + if (window.searchInitialized) return + window.searchInitialized = true -if (searchInput) { - searchInput.addEventListener('input', function(e) { - clearTimeout(searchTimeout); - const query = e.target.value.trim(); + let searchTimeout + const searchInput = document.getElementById('search-input') + const searchDropdown = document.getElementById('search-dropdown') - if (query.length < 2) { - searchDropdown.innerHTML = ''; - return; - } + if (searchInput) { + searchInput.addEventListener('input', function(e) { + clearTimeout(searchTimeout) + const query = e.target.value.trim() - searchTimeout = setTimeout(() => { - fetch('/api/search-quick?q=' + encodeURIComponent(query)) - .then(res => res.json()) - .then(results => { - if (!results || results.length === 0) { - searchDropdown.innerHTML = ''; - return; - } + if (query.length < 2) { + searchDropdown.innerHTML = '' + return + } - let html = '
'; - html += '
Anime
'; - results.forEach(r => { - html += ` - - ${r.image ? `${r.title}` : '
no image
'} -
-
${escapeHtml(r.title)}
-
${r.type}
-
-
- `; - }); - html += `View all results for ${escapeHtml(query)}`; - html += '
'; - searchDropdown.innerHTML = html; - }) - .catch(err => console.error('Search error:', err)); - }, 300); - }); + searchTimeout = setTimeout(() => { + fetch('/api/search-quick?q=' + encodeURIComponent(query)) + .then(res => res.json()) + .then(results => { + if (!results || results.length === 0) { + searchDropdown.innerHTML = '' + return + } - searchInput.addEventListener('blur', () => { - setTimeout(() => { - searchDropdown.innerHTML = ''; - }, 200); - }); + let html = '
' + html += '
Anime
' + results.forEach(r => { + html += ` + + ${r.image ? `${r.title}` : '
no image
'} +
+
${escapeHtml(r.title)}
+
${r.type}
+
+
+ ` + }) + html += `View all results for ${escapeHtml(query)}` + html += '
' + searchDropdown.innerHTML = html + }) + .catch(err => console.error('Search error:', err)) + }, 300) + }) - document.addEventListener('click', (e) => { - if (!e.target.closest('.header-search-wrapper')) { - searchDropdown.innerHTML = ''; - } - }); -} + searchInput.addEventListener('blur', () => { + setTimeout(() => { + searchDropdown.innerHTML = '' + }, 200) + }) -function escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; -} + document.addEventListener('click', (e) => { + if (!e.target.closest('.header-search-wrapper')) { + searchDropdown.innerHTML = '' + } + }) + } + + function escapeHtml(text) { + const div = document.createElement('div') + div.textContent = text + return div.innerHTML + } +})()