diff --git a/web/backend/routes/articles.js b/web/backend/routes/articles.js index 91f6655..6fbaa1b 100644 --- a/web/backend/routes/articles.js +++ b/web/backend/routes/articles.js @@ -1,5 +1,5 @@ import express from 'express'; -import { getNotifiedArticles } from '../services/mongodb.js'; +import { getNotifiedArticles, getArticleFacets } from '../services/mongodb.js'; import { basicAuthMiddleware } from '../middlewares/auth.js'; const router = express.Router(); @@ -43,6 +43,24 @@ router.get('/', basicAuthMiddleware, async (req, res) => { } }); +// Obtener facets (valores únicos) para filtros (requiere autenticación obligatoria) +router.get('/facets', basicAuthMiddleware, async (req, res) => { + try { + // Obtener usuario autenticado (requerido) + const user = req.user; + const isAdmin = user.role === 'admin'; + + // Si no es admin, solo mostrar facets de sus propios artículos + const usernameFilter = isAdmin ? null : user.username; + + const facets = await getArticleFacets(usernameFilter); + + res.json(facets); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + // Buscar artículos en MongoDB (requiere autenticación obligatoria) router.get('/search', basicAuthMiddleware, async (req, res) => { try { diff --git a/web/backend/services/mongodb.js b/web/backend/services/mongodb.js index 2f2b3c8..56a532f 100644 --- a/web/backend/services/mongodb.js +++ b/web/backend/services/mongodb.js @@ -331,6 +331,78 @@ export async function getNotifiedArticles(filter = {}) { } } +export async function getArticleFacets(usernameFilter = null) { + if (!db) { + return { + platforms: [], + usernames: [], + workers: [] + }; + } + + try { + const articlesCollection = db.collection('articles'); + + // Construir query base + const query = {}; + if (usernameFilter) { + // Si hay filtro de username, solo buscar artículos de ese usuario + query['user_info.username'] = usernameFilter; + } + + // Obtener todos los artículos que coincidan con el filtro + const articles = await articlesCollection.find(query).toArray(); + + // Extraer valores únicos + const platformsSet = new Set(); + const usernamesSet = new Set(); + const workersSet = new Set(); + + for (const article of articles) { + // Plataforma (campo directo del artículo) + if (article.platform) { + platformsSet.add(article.platform); + } + + // Username y worker_name (pueden estar en user_info o en campos directos para compatibilidad) + const userInfoList = article.user_info || []; + + if (userInfoList.length > 0) { + // Estructura nueva: usar user_info + for (const userInfo of userInfoList) { + if (userInfo.username) { + usernamesSet.add(userInfo.username); + } + if (userInfo.worker_name) { + workersSet.add(userInfo.worker_name); + } + } + } else { + // Estructura antigua: usar campos directos + if (article.username) { + usernamesSet.add(article.username); + } + if (article.worker_name) { + workersSet.add(article.worker_name); + } + } + } + + return { + platforms: Array.from(platformsSet).sort(), + usernames: Array.from(usernamesSet).sort(), + workers: Array.from(workersSet).sort() + }; + } catch (error) { + console.error('Error obteniendo facets de artículos:', error.message); + return { + platforms: [], + usernames: [], + workers: [] + }; + } +} + export async function getFavorites(username = null) { if (!db) { return []; diff --git a/web/frontend/src/services/api.js b/web/frontend/src/services/api.js index 1b12e6e..05f2183 100644 --- a/web/frontend/src/services/api.js +++ b/web/frontend/src/services/api.js @@ -85,6 +85,11 @@ export default { return response.data; }, + async getArticleFacets() { + const response = await api.get('/articles/facets'); + return response.data; + }, + async searchArticles(query) { const response = await api.get('/articles/search', { params: { q: query }, diff --git a/web/frontend/src/views/Articles.vue b/web/frontend/src/views/Articles.vue index 3a6829d..fe7943d 100644 --- a/web/frontend/src/views/Articles.vue +++ b/web/frontend/src/views/Articles.vue @@ -75,8 +75,9 @@ class="input text-sm w-full" > - - + @@ -256,35 +257,20 @@ const searchTimeout = ref(null); const POLL_INTERVAL = 30000; // 30 segundos const SEARCH_DEBOUNCE = 500; // 500ms de debounce para búsqueda -// Obtener listas de usuarios y workers únicos de los artículos +// Facets obtenidos del backend +const facets = ref({ + platforms: [], + usernames: [], + workers: [] +}); + +// Usar facets del backend en lugar de calcular desde artículos cargados const availableUsernames = computed(() => { - const usernames = new Set(); - allArticles.value.forEach(article => { - if (article.username) { - usernames.add(article.username); - } - }); - searchResults.value.forEach(article => { - if (article.username) { - usernames.add(article.username); - } - }); - return Array.from(usernames).sort(); + return facets.value.usernames || []; }); const availableWorkers = computed(() => { - const workers = new Set(); - allArticles.value.forEach(article => { - if (article.worker_name) { - workers.add(article.worker_name); - } - }); - searchResults.value.forEach(article => { - if (article.worker_name) { - workers.add(article.worker_name); - } - }); - return Array.from(workers).sort(); + return facets.value.workers || []; }); // Artículos que se muestran (búsqueda o lista normal) @@ -315,6 +301,19 @@ function clearAllFilters() { loadArticles(); } +async function loadFacets() { + try { + const data = await api.getArticleFacets(); + facets.value = { + platforms: data.platforms || [], + usernames: data.usernames || [], + workers: data.workers || [] + }; + } catch (error) { + console.error('Error cargando facets:', error); + } +} + async function loadArticles(reset = true, silent = false) { @@ -371,6 +370,7 @@ function handleAuthChange() { selectedUsername.value = ''; } if (currentUser.value) { + loadFacets(); // Recargar facets cuando cambie el usuario loadArticles(); } } @@ -382,6 +382,7 @@ function loadMore() { function handleWSMessage(event) { const data = event.detail; if (data.type === 'articles_updated') { + loadFacets(); // Actualizar facets cuando se actualicen los artículos loadArticles(); } } @@ -447,6 +448,7 @@ onMounted(() => { if (!isAdmin.value && selectedUsername.value) { selectedUsername.value = ''; } + loadFacets(); // Cargar facets primero loadArticles(); window.addEventListener('ws-message', handleWSMessage); window.addEventListener('auth-logout', handleAuthChange); @@ -455,6 +457,7 @@ onMounted(() => { // Iniciar autopoll para actualizar automáticamente autoPollInterval.value = setInterval(() => { loadArticles(true, true); // Reset silencioso cada 30 segundos + loadFacets(); // Actualizar facets también }, POLL_INTERVAL); });