From a34b362683bc75e30c66b7ed04820c1c7a1bc4ad Mon Sep 17 00:00:00 2001 From: aewing Date: Sun, 29 Mar 2026 03:19:52 +0000 Subject: [PATCH] Broaden Tasgird task lookup fallbacks --- server.ts | 105 +++++++++++++++++++++++++++++++++++++++++++++++------ tasks.html | 7 +++- 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/server.ts b/server.ts index 4c985a7..6e118f4 100644 --- a/server.ts +++ b/server.ts @@ -533,16 +533,88 @@ app.get('/api/tasks/list', async (c) => { } const baseFields = 'id,user,title,startDate,dueDate,priority,completed,content,size,status,owner,assigned_to,user_name,Username,expand.user.id,expand.user.email,expand.user.name,expand.user.username'; - const fetchTasksByRelationIds = async (userIds: string[]) => { + const uniqueById = (records: any[]) => { + const seen = new Map(); + for (const record of Array.isArray(records) ? records : []) { + const id = String(record?.id || '').trim(); + if (!id || seen.has(id)) continue; + seen.set(id, record); + } + return Array.from(seen.values()); + }; + + const taskMatchesTarget = (task: any, userIds: string[], nameHints: string[]) => { + const idSet = new Set(userIds.map((id) => String(id || '').trim()).filter(Boolean)); + const lowerHints = nameHints.map((value) => String(value || '').trim().toLowerCase()).filter(Boolean); + + const directUsers = Array.isArray(task?.user) + ? task.user + : (task?.user ? [task.user] : []); + if (directUsers.some((value: any) => idSet.has(String(value || '').trim()))) { + return true; + } + + const expandedUsers = Array.isArray(task?.expand?.user) + ? task.expand.user + : (task?.expand?.user ? [task.expand.user] : []); + if (expandedUsers.some((user: any) => idSet.has(String(user?.id || '').trim()))) { + return true; + } + + const textFields = [task?.user_name, task?.Username, task?.owner, task?.assigned_to] + .map((value) => String(value || '').trim().toLowerCase()) + .filter(Boolean); + + return lowerHints.some((hint) => textFields.some((value) => value === hint || value.includes(hint))); + }; + + const fetchTasksByRelationIds = async (userIds: string[], nameHints: string[]) => { const ids = Array.from(new Set(userIds.map((id) => String(id || '').trim()).filter(Boolean))); - if (!ids.length) return []; - const relationFilter = ids.map((id) => `user = "${id.replace(/"/g, '\\"')}"`).join(' || '); - return taskPb.collection(PB_TASKS_COLLECTION).getFullList({ - filter: relationFilter, - sort: '-startDate,-created', - expand: 'user', - fields: baseFields, - }); + if (!ids.length) return { records: [], lookupMethod: 'no_user_ids' }; + + const filterAttempts = [ + { + lookupMethod: 'relation_eq', + filter: ids.map((id) => `user = "${id.replace(/"/g, '\\"')}"`).join(' || '), + }, + { + lookupMethod: 'relation_any_eq', + filter: ids.map((id) => `user ?= "${id.replace(/"/g, '\\"')}"`).join(' || '), + }, + ]; + + let lastError: unknown = null; + for (const attempt of filterAttempts) { + try { + const records = await taskPb.collection(PB_TASKS_COLLECTION).getFullList({ + filter: attempt.filter, + sort: '-startDate,-created', + expand: 'user', + fields: baseFields, + }); + if (records.length) { + return { records: uniqueById(records), lookupMethod: attempt.lookupMethod }; + } + } catch (error) { + lastError = error; + } + } + + try { + const accessibleTasks = await taskPb.collection(PB_TASKS_COLLECTION).getFullList({ + sort: '-startDate,-created', + expand: 'user', + fields: baseFields, + }); + const filtered = accessibleTasks.filter((task: any) => taskMatchesTarget(task, ids, nameHints)); + return { + records: uniqueById(filtered), + lookupMethod: 'accessible_fallback', + }; + } catch (error) { + if (lastError) throw lastError; + throw error; + } }; const resolveUserIdsByName = async (name: string): Promise => { @@ -587,6 +659,7 @@ app.get('/api/tasks/list', async (c) => { let warning = ''; let effectiveUserId = requestedUserId || meId; let effectiveUserName = targetUserName; + let lookupMethod = 'unresolved'; try { if (targetUserName) { const targetUserIds = Array.from(new Set([ @@ -598,7 +671,9 @@ app.get('/api/tasks/list', async (c) => { warning = `No Users record matched name "${targetUserName}".`; records = []; } else { - records = await fetchTasksByRelationIds(targetUserIds); + const result = await fetchTasksByRelationIds(targetUserIds, [targetUserName, meName]); + records = result.records; + lookupMethod = result.lookupMethod; if (!records.length) { warning = `No accessible Tasgird tasks matched Users.name "${targetUserName}".`; } @@ -606,7 +681,9 @@ app.get('/api/tasks/list', async (c) => { } } else if (requestedUserId || meId) { const fallbackId = String(requestedUserId || meId).trim(); - records = await fetchTasksByRelationIds([fallbackId]); + const result = await fetchTasksByRelationIds([fallbackId], [meName]); + records = result.records; + lookupMethod = result.lookupMethod; if (!records.length) { warning = 'No accessible Tasgird tasks matched the current user relation.'; } @@ -628,7 +705,13 @@ app.get('/api/tasks/list', async (c) => { success: true, userId: effectiveUserId, userName: effectiveUserName, + authUser: { + id: meId, + email: meRecord?.email || '', + name: meName, + }, count: records.length, + lookupMethod, warning: warning || undefined, tasks: records.map((task: any) => ({ id: task.id, diff --git a/tasks.html b/tasks.html index bda1f51..4321eda 100644 --- a/tasks.html +++ b/tasks.html @@ -387,7 +387,10 @@ }); const data = await resp.json().catch(() => ({})); if (!resp.ok || !data?.success) { - throw new Error(data?.message || 'Failed to load tasks'); + const details = data?.details + ? (typeof data.details === 'string' ? data.details : JSON.stringify(data.details)) + : ''; + throw new Error(details ? `${data?.message || 'Failed to load tasks'}: ${details}` : (data?.message || 'Failed to load tasks')); } if (data?.userName && taskUserSelect.value !== data.userName) { taskUserSelect.value = data.userName; @@ -397,6 +400,8 @@ message: `Loaded ${data?.count ?? 0} task(s)`, userName: data?.userName || userName, userId: data?.userId || undefined, + authUser: data?.authUser || undefined, + lookupMethod: data?.lookupMethod || undefined, warning: data?.warning || undefined, tasks: data.tasks || [], });