- Remove apps/dispatch/ (100% replaced by ops dispatch module, unmaintained) - Commit services/targo-hub/lib/ (24 modules, 6290 lines — was never tracked) - Commit services/docuseal + services/legacy-db docker-compose configs - Extract client app composables: useOTP, useAddressSearch, catalog data, format utils - Refactor CartPage.vue 630→175 lines, CatalogPage.vue 375→95 lines - Clean hardcoded credentials from config.js fallback values - Add client portal: catalog, cart, checkout, OTP verification, address search - Add ops: NetworkPage, AgentFlowsPage, ConversationPanel, UnifiedCreateModal - Add ops composables: useBestTech, useConversations, usePermissions, useScanner - Add field app: scanner composable, docker/nginx configs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
68 lines
2.1 KiB
JavaScript
68 lines
2.1 KiB
JavaScript
'use strict'
|
|
const cfg = require('./config')
|
|
const { log, json, parseBody } = require('./helpers')
|
|
|
|
const authHeader = 'Basic ' + Buffer.from(cfg.TRACCAR_USER + ':' + cfg.TRACCAR_PASS).toString('base64')
|
|
|
|
async function traccarFetch (path) {
|
|
const res = await fetch(cfg.TRACCAR_URL + path, {
|
|
headers: { Authorization: authHeader, Accept: 'application/json' },
|
|
})
|
|
if (!res.ok) throw new Error(`Traccar ${res.status}: ${path}`)
|
|
return res.json()
|
|
}
|
|
|
|
// Cache devices for 60s (they rarely change)
|
|
let _devCache = null, _devCacheTs = 0
|
|
async function getDevices () {
|
|
if (_devCache && Date.now() - _devCacheTs < 60000) return _devCache
|
|
_devCache = await traccarFetch('/api/devices?all=true')
|
|
_devCacheTs = Date.now()
|
|
return _devCache
|
|
}
|
|
|
|
async function getPositions (deviceIds) {
|
|
if (!deviceIds?.length) return []
|
|
const results = await Promise.allSettled(
|
|
deviceIds.map(id => traccarFetch('/api/positions?deviceId=' + id))
|
|
)
|
|
return results.flatMap(r => r.status === 'fulfilled' ? r.value : [])
|
|
}
|
|
|
|
async function handle (req, res, method, path) {
|
|
const sub = path.replace('/traccar', '').replace(/^\//, '')
|
|
|
|
if (sub === 'devices' && method === 'GET') {
|
|
try {
|
|
return json(res, 200, await getDevices())
|
|
} catch (e) {
|
|
log('Traccar devices error:', e.message)
|
|
return json(res, 502, { error: 'Traccar unavailable' })
|
|
}
|
|
}
|
|
|
|
if (sub === 'positions' && method === 'GET') {
|
|
try {
|
|
const url = new URL(req.url, 'http://localhost')
|
|
const ids = (url.searchParams.get('deviceIds') || '').split(',').map(Number).filter(Boolean)
|
|
return json(res, 200, await getPositions(ids))
|
|
} catch (e) {
|
|
log('Traccar positions error:', e.message)
|
|
return json(res, 502, { error: 'Traccar unavailable' })
|
|
}
|
|
}
|
|
|
|
// Proxy any other Traccar API path (fallback)
|
|
if (method === 'GET') {
|
|
try {
|
|
return json(res, 200, await traccarFetch('/api/' + sub))
|
|
} catch (e) {
|
|
return json(res, 502, { error: e.message })
|
|
}
|
|
}
|
|
|
|
json(res, 404, { error: 'Traccar endpoint not found' })
|
|
}
|
|
|
|
module.exports = { handle, getDevices, getPositions }
|