Split SambaPOS into separate SQL + GraphQL credential sets, add both connection tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-14 13:09:04 +00:00
parent 4f6fe6bb37
commit f185b73678
2 changed files with 47 additions and 2 deletions

View file

@ -24,8 +24,8 @@ export const INTEGRATIONS = {
}, },
sambapos: { sambapos: {
name: 'SambaPOS', name: 'SambaPOS',
configFields: ['host', 'port', 'database', 'graphql_endpoint', 'username'], configFields: ['sql_host', 'sql_port', 'sql_database', 'sql_username', 'graphql_endpoint', 'graphql_username', 'graphql_client_id'],
secretFields: ['password'], secretFields: ['sql_password', 'graphql_password'],
}, },
workforce: { workforce: {
name: 'Workforce', name: 'Workforce',

View file

@ -139,6 +139,51 @@ export async function integrationRoutes(app) {
if (res.status === 401 || res.status === 403) throw new Error('Invalid Azure API key') if (res.status === 401 || res.status === 403) throw new Error('Invalid Azure API key')
if (!res.ok) throw new Error(`Azure returned ${res.status}`) if (!res.ok) throw new Error(`Azure returned ${res.status}`)
} }
else if (req.params.slug === 'sambapos') {
const errors = []
// SQL test
try {
if (!creds.sql_host || !creds.sql_database || !creds.sql_username || !creds.sql_password)
throw new Error('SQL host, database, username and password are required')
const { default: pg } = await import('pg')
const sqlPool = new pg.Pool({
host: creds.sql_host, port: parseInt(creds.sql_port || '5432'),
database: creds.sql_database, user: creds.sql_username, password: creds.sql_password,
connectionTimeoutMillis: 8000, max: 1,
})
await sqlPool.query('SELECT 1')
await sqlPool.end()
} catch (e) { errors.push(`SQL: ${e.message}`) }
// GraphQL test — obtain token via password grant then run a minimal query
try {
if (!creds.graphql_endpoint || !creds.graphql_username || !creds.graphql_password || !creds.graphql_client_id)
throw new Error('GraphQL endpoint, username, password and client ID are required')
const base = creds.graphql_endpoint.replace(/\/$/, '')
const tokenRes = await fetch(`${base}/connect/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'password',
username: creds.graphql_username,
password: creds.graphql_password,
client_id: creds.graphql_client_id,
scope: 'api',
}),
signal: AbortSignal.timeout(8000),
})
if (!tokenRes.ok) throw new Error(`Token request failed (${tokenRes.status})`)
const { access_token } = await tokenRes.json()
if (!access_token) throw new Error('No access token returned')
const gqlRes = await fetch(`${base}/api`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${access_token}` },
body: JSON.stringify({ query: '{ __typename }' }),
signal: AbortSignal.timeout(8000),
})
if (!gqlRes.ok) throw new Error(`GraphQL request failed (${gqlRes.status})`)
} catch (e) { errors.push(`GraphQL: ${e.message}`) }
if (errors.length) throw new Error(errors.join(' | '))
}
else return reply.status(400).send({ error: `No test available for ${req.params.slug}` }) else return reply.status(400).send({ error: `No test available for ${req.params.slug}` })
return { ok: true } return { ok: true }
} catch (e) { } catch (e) {