const SETTINGS_URL = process.env.SETTINGS_URL || 'http://10.10.10.106:3080' const SETTINGS_SECRET = process.env.SETTINGS_SECRET || '' let _credsCache = null // { creds, expires_at } async function getWorkforceCreds() { if (_credsCache && Date.now() < _credsCache.expires_at) return _credsCache.creds const res = await fetch(`${SETTINGS_URL}/settings/api/internal/integration/workforce`, { headers: { Authorization: `Bearer ${SETTINGS_SECRET}` }, signal: AbortSignal.timeout(5000), }) if (!res.ok) throw new Error(`Failed to fetch Workforce credentials from settings: ${res.status}`) const creds = await res.json() if (!creds.bearer_token) throw new Error('Workforce bearer token not configured in settings') _credsCache = { creds, expires_at: Date.now() + 5 * 60_000 } return creds } async function wfFetch(path) { const creds = await getWorkforceCreds() const baseUrl = creds.base_url || 'https://my.workforce.com' const res = await fetch(`${baseUrl}${path}`, { headers: { Authorization: `Bearer ${creds.bearer_token}` }, signal: AbortSignal.timeout(10000), }) if (!res.ok) { const body = await res.text().catch(() => '') throw new Error(`Workforce API error ${res.status} at ${path}${body ? ': ' + body.slice(0, 200) : ''}`) } return res.json() } async function wfFetchPaged(path) { const results = [] let page = 1 while (true) { const sep = path.includes('?') ? '&' : '?' const data = await wfFetch(`${path}${sep}page=${page}&page_size=100`) const items = Array.isArray(data) ? data : (data.users ?? data.departments ?? data.teams ?? data.locations ?? []) results.push(...items) if (items.length < 100) break page++ } return results } export async function findEmployeeByEmail(email) { const data = await wfFetch(`/api/v2/users?email=${encodeURIComponent(email)}`) const users = Array.isArray(data) ? data : (data.users ?? []) return users.length > 0 ? users[0] : null } export async function findEmployeeById(wfUserId) { try { const data = await wfFetch(`/api/v2/users/${wfUserId}`) return data?.id ? data : (data?.user ?? null) } catch { return null } } export async function getLocationsByIds(ids) { const results = await Promise.all(ids.map(async id => { try { const data = await wfFetch(`/api/v2/locations/${id}`) return { id: String(id), name: data.name ?? `Location ${id}`, short_name: data.short_name ?? null } } catch { return { id: String(id), name: `Location ${id}`, short_name: null } } })) return results } export async function getAllDepartments() { return wfFetchPaged('/api/v2/departments') } export async function getEmployeeDepartments(wfUserId) { const depts = await getAllDepartments() const id = String(wfUserId) return depts.filter(d => { const staff = (d.staff ?? []).map(String) const managers = (d.managers ?? []).map(String) return staff.includes(id) || managers.includes(id) }) } export async function getSyncConfig() { const creds = await getWorkforceCreds() return { syncHours: parseFloat(creds.sync_hours ?? '6'), locationId: creds.location_id ? String(creds.location_id) : null, } } export function invalidateCache() { _credsCache = null }