gigafibre-fsm/services/targo-hub/lib/telephony.js
louispaulb 320655b0a0 refactor: major cleanup — remove dead dispatch app, commit all backend code, extract client composables
- 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>
2026-04-08 17:38:38 -04:00

102 lines
4.7 KiB
JavaScript

'use strict'
const cfg = require('./config')
const { log, json, parseBody } = require('./helpers')
let routrPool = null, identityPool = null
function getPool (connStr, label) {
const { Pool } = require('pg')
const pool = new Pool({ connectionString: connStr, max: 3 })
pool.on('error', e => log(`${label} DB pool error:`, e.message))
return pool
}
function getRoutrPool () { return routrPool || (routrPool = getPool(cfg.ROUTR_DB_URL, 'Routr')) }
function getIdentityPool () { return identityPool || (identityPool = getPool(cfg.FNIDENTITY_DB_URL, 'Identity')) }
const ROUTR_TABLES = { trunks: 'trunks', agents: 'agents', credentials: 'credentials', numbers: 'numbers', domains: 'domains', acls: 'access_control_lists', peers: 'peers' }
const IDENTITY_TABLES = { workspaces: 'workspaces', users: 'users' }
async function handle (req, res, method, path, url) {
try {
const parts = path.replace('/telephony/', '').split('/').filter(Boolean)
const resource = parts[0], ref = parts[1] || null
if (resource === 'overview' && method === 'GET') {
const rPool = getRoutrPool(), iPool = getIdentityPool()
const [trunks, agents, creds, numbers, domains, peers, workspaces] = await Promise.all([
rPool.query('SELECT COUNT(*) FROM trunks'),
rPool.query('SELECT COUNT(*) FROM agents'),
rPool.query('SELECT COUNT(*) FROM credentials'),
rPool.query('SELECT COUNT(*) FROM numbers'),
rPool.query('SELECT COUNT(*) FROM domains'),
rPool.query('SELECT COUNT(*) FROM peers'),
iPool.query('SELECT COUNT(*) FROM workspaces'),
])
return json(res, 200, {
trunks: +trunks.rows[0].count, agents: +agents.rows[0].count,
credentials: +creds.rows[0].count, numbers: +numbers.rows[0].count,
domains: +domains.rows[0].count, peers: +peers.rows[0].count,
workspaces: +workspaces.rows[0].count,
})
}
const isIdentity = !!IDENTITY_TABLES[resource]
const tableName = IDENTITY_TABLES[resource] || ROUTR_TABLES[resource]
if (!tableName) {
return json(res, 404, { error: `Unknown resource: ${resource}. Available: overview, ${[...Object.keys(ROUTR_TABLES), ...Object.keys(IDENTITY_TABLES)].join(', ')}` })
}
const pool = isIdentity ? getIdentityPool() : getRoutrPool()
if (method === 'GET' && !ref) {
const limit = parseInt(url.searchParams.get('limit') || '100', 10)
const offset = parseInt(url.searchParams.get('offset') || '0', 10)
const result = await pool.query(`SELECT * FROM ${tableName} ORDER BY created_at DESC LIMIT $1 OFFSET $2`, [limit, offset])
const count = await pool.query(`SELECT COUNT(*) FROM ${tableName}`)
return json(res, 200, { items: result.rows, total: +count.rows[0].count })
}
if (method === 'GET' && ref) {
const result = await pool.query(`SELECT * FROM ${tableName} WHERE ref = $1`, [ref])
return result.rows.length ? json(res, 200, result.rows[0]) : json(res, 404, { error: 'Not found' })
}
if (method === 'POST' && !ref) {
const body = await parseBody(req)
if (!body.ref) body.ref = require('crypto').randomUUID()
if (!body.api_version) body.api_version = 'v2'
if (!body.created_at) body.created_at = new Date()
if (!body.updated_at) body.updated_at = new Date()
const keys = Object.keys(body), vals = Object.values(body)
const result = await pool.query(`INSERT INTO ${tableName} (${keys.join(', ')}) VALUES (${keys.map((_, i) => `$${i + 1}`).join(', ')}) RETURNING *`, vals)
log(`Telephony: created ${resource}/${result.rows[0].ref}`)
return json(res, 201, result.rows[0])
}
if (method === 'PUT' && ref) {
const body = await parseBody(req)
body.updated_at = new Date(); delete body.ref; delete body.created_at
const keys = Object.keys(body), vals = Object.values(body)
vals.push(ref)
const result = await pool.query(`UPDATE ${tableName} SET ${keys.map((k, i) => `${k} = $${i + 1}`).join(', ')} WHERE ref = $${vals.length} RETURNING *`, vals)
if (!result.rows.length) return json(res, 404, { error: 'Not found' })
log(`Telephony: updated ${resource}/${ref}`)
return json(res, 200, result.rows[0])
}
if (method === 'DELETE' && ref) {
const result = await pool.query(`DELETE FROM ${tableName} WHERE ref = $1 RETURNING ref`, [ref])
if (!result.rows.length) return json(res, 404, { error: 'Not found' })
log(`Telephony: deleted ${resource}/${ref}`)
return json(res, 200, { ok: true, deleted: ref })
}
return json(res, 405, { error: 'Method not allowed' })
} catch (e) {
log('Telephony error:', e.message)
return json(res, 500, { error: e.message })
}
}
module.exports = { handle }