fix(CG6): loadChannels() matcher /notify/channels API-format (OQ-29)

This commit is contained in:
chrischristiansen-glitch 2026-06-15 06:59:56 +02:00
parent bf366f89b0
commit 6367683d81

View File

@ -310,7 +310,6 @@ function renderStats(sum, anomCount){
const cost = sum?.estimated_cost_usd ?? null
const budget = sum?.budget_limit ?? null
// Infer tier from cost (approximate GCP spend = cost * 100)
const gcpSpend = cost != null ? cost * 100 : 0
const tier = TIER_MAP.find(t => gcpSpend < t.max) || TIER_MAP[TIER_MAP.length-1]
document.getElementById('tier-badge').textContent = tier.name
@ -394,7 +393,6 @@ function renderModules(data){
<tbody>${rows}</tbody>
</table>`
// Donut
const canvas = document.getElementById('donut-canvas')
if(donutChart) donutChart.destroy()
donutChart = new Chart(canvas,{
@ -418,33 +416,41 @@ function renderModules(data){
}
// ── DELIVERY CHANNELS ─────────────────────────────────────────────────────────
// Matcher /notify/channels respons: { channels: [{type, configured, target, url}] }
const CH_META = {
webhook: { icon:'🔗', name:'Webhook', tier:'Starter+' },
email: { icon:'📧', name:'E-post', tier:'Starter+' },
sms: { icon:'📱', name:'SMS', tier:'Guard+' },
}
async function loadChannels(){
const el = document.getElementById('ch-list')
// Fetch live channel config — falls back to defaults if endpoint not yet wired
let channels = null
let apiChannels = []
try{
const r = await fetch('/notify/channels')
if(r.ok) channels = await r.json()
if(r.ok){
const data = await r.json()
apiChannels = data.channels || []
}
}catch(_){}
// Sensible defaults with live endpoint awareness
const defs = [
{ icon:'🔗', name:'Webhook', key:'webhook', tier:'Starter+', endpoint:'POST /notify/webhook' },
{ icon:'📧', name:'E-post', key:'email', tier:'Starter+', endpoint:'POST /notify/email' },
{ icon:'📱', name:'SMS', key:'sms', tier:'Guard+', endpoint:'POST /notify/sms' },
]
el.innerHTML = '<div class="ch-list">' + defs.map(ch => {
const cfg = channels?.[ch.key]
const active = cfg?.active ?? (ch.key==='webhook'||ch.key==='email') // CG5a+CG5b live
const detail = cfg?.destination || cfg?.url || cfg?.address || ch.endpoint
if(!apiChannels.length){
el.innerHTML = '<div class="loader" style="color:var(--text-faint)">Kanaler ikke tilgjengelig</div>'
return
}
el.innerHTML = '<div class="ch-list">' + apiChannels.map(ch => {
const meta = CH_META[ch.type] || { icon:'📡', name:ch.type, tier:'' }
const active = ch.configured === true
const detail = ch.url || ch.target || `POST /notify/${ch.type}`
return `<div class="ch-item">
<div class="ch-icon">${ch.icon}</div>
<div class="ch-icon">${meta.icon}</div>
<div class="ch-info">
<div class="ch-name">${ch.name} <span style="font-size:10px;color:var(--text-faint);font-family:var(--mono);font-weight:400">${ch.tier}</span></div>
<div class="ch-name">${meta.name} <span style="font-size:10px;color:var(--text-faint);font-family:var(--mono);font-weight:400">${meta.tier}</span></div>
<div class="ch-detail">${esc(detail)}</div>
</div>
<div class="ch-status ${active?'active':'inactive'}">${active?'Aktiv':'Ikke konfigurert'}</div>
${active?`<button class="ch-test-btn" onclick="testChannel('${ch.key}')">Test</button>`:''}
${active?`<button class="ch-test-btn" onclick="testChannel('${ch.type}')">Test</button>`:''}
</div>`
}).join('') + '</div>'
}
@ -452,7 +458,10 @@ async function loadChannels(){
async function testChannel(key){
toast('Sender testmelding via '+key+'...')
try{
const r = await fetch('/notify/'+key,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({test:true,message:'CostGuard testmelding fra klient-dashboard'})})
const body = key==='webhook'
? JSON.stringify({url:'', event:'custom', title:'CostGuard testmelding', body:'Test fra klient-dashboard'})
: JSON.stringify({event:'custom', subject:'CostGuard testmelding', body_html:'<p>Test fra klient-dashboard</p>'})
const r = await fetch('/notify/'+key,{method:'POST',headers:{'Content-Type':'application/json'},body})
if(r.ok) toast('✅ Testmelding sendt via '+key)
else toast('⚠️ Svarte '+r.status+' — sjekk konfig')
}catch(e){