From 6abaf6c77811bb10d0e15d7d0a200450fcd2ba39 Mon Sep 17 00:00:00 2001 From: aewing Date: Sun, 29 Mar 2026 02:51:14 +0000 Subject: [PATCH] Use user name for tasks lookup with robust fallbacks --- server.ts | 82 ++++++++++++++++++++++++++++++++++++++++++++++-------- tasks.html | 26 +++++++++-------- 2 files changed, 86 insertions(+), 22 deletions(-) diff --git a/server.ts b/server.ts index c1d4368..0bd4e2f 100644 --- a/server.ts +++ b/server.ts @@ -495,39 +495,98 @@ app.get('/api/tasks/list', async (c) => { try { const me = pb.authStore.model; const meId = String(me?.id || '').trim(); + const meName = String(me?.name || me?.username || me?.email || '').trim(); + const requestedUserName = String(c.req.query('userName') || '').trim(); const requestedUserId = String(c.req.query('userId') || '').trim(); - const targetUserId = requestedUserId || meId; - if (!targetUserId) return c.json({ success: false, message: 'No target user available' }, 400); + const targetUserName = requestedUserName || meName; + if (!targetUserName && !requestedUserId && !meId) { + return c.json({ success: false, message: 'No target user available' }, 400); + } - const fetchTasks = async (userId: string) => { - const filter = `user = "${userId.replace(/"/g, '\\"')}"`; + const baseFields = 'id,user,title,startDate,dueDate,priority,completed,content,size,status,expand.user.id,expand.user.email,expand.user.name,expand.user.username'; + const fetchTasksWithFilter = async (filter: string) => { return pb.collection(PB_TASKS_COLLECTION).getFullList({ filter, sort: '-startDate,-created', expand: 'user', - fields: 'id,user,title,startDate,dueDate,priority,completed,content,size,status,expand.user.id,expand.user.email,expand.user.name,expand.user.username', + fields: baseFields, }); }; + const fetchAllTasks = async () => { + return pb.collection(PB_TASKS_COLLECTION).getFullList({ + sort: '-startDate,-created', + expand: 'user', + fields: baseFields, + }); + }; + + const matchTaskByName = (task: any, name: string): boolean => { + const target = String(name || '').trim().toLowerCase(); + if (!target) return false; + const taskUserDisplay = String(task?.expand?.user?.name || task?.expand?.user?.username || task?.expand?.user?.email || '').trim().toLowerCase(); + const taskUserRaw = String(task?.user || '').trim().toLowerCase(); + const owner = String(task?.owner || '').trim().toLowerCase(); + const assignedTo = String(task?.assigned_to || '').trim().toLowerCase(); + const userName = String(task?.user_name || task?.Username || '').trim().toLowerCase(); + return [taskUserDisplay, taskUserRaw, owner, assignedTo, userName].some((v) => v && v === target); + }; + let records: any[] = []; let warning = ''; - let effectiveUserId = targetUserId; + let effectiveUserId = requestedUserId || meId; + let effectiveUserName = targetUserName; try { - records = await fetchTasks(targetUserId); - } catch (listErr) { - if (targetUserId !== meId && meId) { + const escapedName = targetUserName.replace(/"/g, '\\"'); + const escapedUserId = (requestedUserId || meId).replace(/"/g, '\\"'); + const candidateFilters = [ + targetUserName ? `user = "${escapedName}"` : '', + targetUserName ? `user_name = "${escapedName}"` : '', + targetUserName ? `Username = "${escapedName}"` : '', + targetUserName ? `assigned_to = "${escapedName}"` : '', + targetUserName ? `owner = "${escapedName}"` : '', + escapedUserId ? `user = "${escapedUserId}"` : '', + ].filter(Boolean); + + for (const filter of candidateFilters) { try { - records = await fetchTasks(meId); + const found = await fetchTasksWithFilter(filter); + if (Array.isArray(found) && found.length) { + records = found; + break; + } + } catch { + } + } + + if (!records.length) { + const all = await fetchAllTasks(); + if (targetUserName) { + records = all.filter((task: any) => matchTaskByName(task, targetUserName)); + } else if (escapedUserId) { + records = all.filter((task: any) => String(task?.user || '') === escapedUserId); + } else { + records = all; + } + } + } catch (listErr) { + if (targetUserName && meName && targetUserName !== meName) { + try { + const all = await fetchAllTasks(); + records = all.filter((task: any) => matchTaskByName(task, meName)); effectiveUserId = meId; + effectiveUserName = meName; warning = 'Requested user tasks are restricted by PocketBase rules; showing your tasks instead.'; } catch { records = []; effectiveUserId = meId; + effectiveUserName = meName; warning = 'Task list is restricted by PocketBase rules; no accessible tasks for current user.'; } } else { records = []; - effectiveUserId = meId || targetUserId; + effectiveUserId = meId || requestedUserId; + effectiveUserName = targetUserName || meName; warning = 'Task list is restricted by PocketBase rules; no accessible tasks for current user.'; } } @@ -535,6 +594,7 @@ app.get('/api/tasks/list', async (c) => { return c.json({ success: true, userId: effectiveUserId, + userName: effectiveUserName, count: records.length, warning: warning || undefined, tasks: records.map((task: any) => ({ diff --git a/tasks.html b/tasks.html index 4ecb0c9..7abbc36 100644 --- a/tasks.html +++ b/tasks.html @@ -317,22 +317,25 @@ usersCache = Array.isArray(data.users) ? data.users : []; const meId = String(data?.me?.id || '').trim(); const meName = data?.me?.name || data?.me?.email || 'Me'; + const meLookupName = String(data?.me?.name || data?.me?.email || '').trim(); taskUserSelect.innerHTML = ''; for (const user of usersCache) { const opt = document.createElement('option'); - opt.value = user.id; + const lookupName = String(user.name || user.email || user.id || '').trim(); + opt.value = lookupName; + opt.dataset.userId = user.id; opt.textContent = `${user.name || user.email || user.id}${user.id === meId ? ' (Me)' : ''}`; taskUserSelect.appendChild(opt); } - if (meId) { - taskUserSelect.value = meId; + if (meLookupName) { + taskUserSelect.value = meLookupName; } - writeOutput({ message: `Loaded ${usersCache.length} users`, me: { id: meId, name: meName } }); + writeOutput({ message: `Loaded ${usersCache.length} users`, me: { id: meId, name: meName, lookupName: meLookupName } }); if (data?.warning) { - writeOutput({ message: `Loaded ${usersCache.length} users`, warning: data.warning, me: { id: meId, name: meName } }); + writeOutput({ message: `Loaded ${usersCache.length} users`, warning: data.warning, me: { id: meId, name: meName, lookupName: meLookupName } }); } } @@ -367,9 +370,9 @@ async function loadTasks() { await ensurePocketBaseClient(); const token = getCurrentToken(); - const userId = String(taskUserSelect.value || '').trim(); - const url = userId - ? `/api/tasks/list?userId=${encodeURIComponent(userId)}` + const userName = String(taskUserSelect.value || '').trim(); + const url = userName + ? `/api/tasks/list?userName=${encodeURIComponent(userName)}` : '/api/tasks/list'; const resp = await fetch(url, { headers: { 'x-pb-token': token }, @@ -378,13 +381,14 @@ if (!resp.ok || !data?.success) { throw new Error(data?.message || 'Failed to load tasks'); } - if (data?.userId && taskUserSelect.value !== data.userId) { - taskUserSelect.value = data.userId; + if (data?.userName && taskUserSelect.value !== data.userName) { + taskUserSelect.value = data.userName; } renderTasks(data.tasks || []); writeOutput({ message: `Loaded ${data?.count ?? 0} task(s)`, - userId: data?.userId || userId, + userName: data?.userName || userName, + userId: data?.userId || undefined, warning: data?.warning || undefined, tasks: data.tasks || [], });