CG3: park AWS billing UI and make history endpoint best-effort so GCP dashboard stays live

This commit is contained in:
chrischristiansen-glitch 2026-06-09 03:15:20 +02:00
parent 88037bdba2
commit 868a0e496d

View File

@ -204,9 +204,7 @@ footer{padding:10px 20px 16px;font-size:11px;color:var(--faint);text-align:cente
</div>
<div class="tb-right">
<div id="provider-toggle" class="sel" style="display:flex; gap: 4px; padding: 2px;">
<button class="sel-opt active" data-provider="gcp" onclick="switchProvider('gcp')">GCP</button>
<button class="sel-opt" data-provider="aws" onclick="switchProvider('aws')">AWS</button>
<button class="sel-opt" data-provider="all" onclick="switchProvider('all')">Alle</button>
<span class="sel-opt active" data-provider="gcp">GCP</span>
</div>
<span id="push-badge" class="badge" style="display:none; background: #333; color: #aaa;">🔔 Varsler aktive</span>
<span class="pulse" id="live-dot"></span>
@ -432,7 +430,7 @@ footer{padding:10px 20px 16px;font-size:11px;color:var(--faint);text-align:cente
const API_BASE = '';
let currentBudget = 500;
const REFRESH_MS = 5 * 60 * 1000;
let currentProvider = localStorage.getItem('costguard_provider') || 'gcp';
let currentProvider = 'gcp';
const DEMO_SUMMARY = [
{service:'Cloud Run',total_cost:1.82},{service:'Cloud Build',total_cost:0.61},
@ -444,7 +442,7 @@ const DEMO_FORECAST = {month_to_date_cost:3.00,daily_average_last_7_days:0.13,to
const DEMO_ANOMALIES = [];
let barChart,donutChart,period=30;
let currentData={gcp: null, aws: null};
let currentData={gcp: null};
let userProfile = null;
function fmtNOK(v,d=2){
@ -469,10 +467,11 @@ async function apiFetch(url, options = {}) {
}
function switchProvider(provider) {
currentProvider = provider;
localStorage.setItem('costguard_provider', provider);
// AWS-støtte er parkert; vi holder oss på GCP
currentProvider = 'gcp';
localStorage.setItem('costguard_provider', 'gcp');
document.querySelectorAll('#provider-toggle .sel-opt').forEach(btn => {
btn.classList.toggle('active', btn.dataset.provider === provider);
btn.classList.toggle('active', btn.dataset.provider === 'gcp');
});
fetchAll();
}
@ -562,20 +561,28 @@ async function fetchAll(){
const gcpForecast = apiFetch(API_BASE+'/billing/live');
const gcpAnomalies = apiFetch(API_BASE+'/billing/anomalies');
const gcpHistory = apiFetch(API_BASE+'/billing/history');
const awsSummary = apiFetch(API_BASE+'/billing/aws/summary');
const awsForecast = apiFetch(API_BASE+'/billing/aws/forecast');
const [gcpSR, gcpLR, gcpAR, gcpHR, awsSR, awsLR] = await Promise.all([gcpSummary, gcpForecast, gcpAnomalies, gcpHistory, awsSummary, awsForecast]);
const [gcpSR, gcpLR, gcpAR, gcpHR] = await Promise.all([gcpSummary, gcpForecast, gcpAnomalies, gcpHistory]);
if (!gcpSR.ok || !gcpLR.ok || !gcpAR.ok || !gcpHR.ok || !awsSR.ok || !awsLR.ok) {
throw new Error('HTTP error on one or more endpoints');
// GCP summary/forecast/anomalies er kritiske; history er nice-to-have
if (!gcpSR.ok || !gcpLR.ok || !gcpAR.ok) {
throw new Error('HTTP error on core billing endpoints');
}
const [gcpS, gcpL, gcpA, gcpH, awsS, awsL] = await Promise.all([gcpSR.json(), gcpLR.json(), gcpAR.json(), gcpHR.json(), awsSR.json(), awsLR.json()]);
const [gcpS, gcpL, gcpA] = await Promise.all([gcpSR.json(), gcpLR.json(), gcpAR.json()]);
let gcpH = { history: [] };
if (gcpHR.ok) {
try {
gcpH = await gcpHR.json();
} catch (e) {
console.warn('Could not parse history JSON:', e.message);
}
} else {
console.warn('History endpoint returned', gcpHR.status);
}
currentData.gcp = { summary: gcpS.summary || [], forecast: gcpL, anomalies: gcpA.anomalies || [], history: gcpH.history || [] };
currentData.aws = { summary: awsS.summary || [], forecast: awsL, anomalies: [], history: [] };
document.getElementById('err-banner').classList.remove('show');
dot.style.background='var(--ok)';
@ -583,7 +590,6 @@ async function fetchAll(){
console.warn('API feil, demo-data:',err.message);
if (err.message.includes("Not logged in")) return;
currentData.gcp = { summary: DEMO_SUMMARY, forecast: DEMO_FORECAST, anomalies: DEMO_ANOMALIES, history: [] };
currentData.aws = { summary: [], forecast: {}, anomalies: [], history: [] };
document.getElementById('err-msg').textContent='API utilgjengelig: '+err.message+' — demo-data vises.';
document.getElementById('err-banner').classList.add('show');
dot.style.background='var(--red)';
@ -593,43 +599,8 @@ async function fetchAll(){
}
function getVisibleData() {
if (currentProvider === 'gcp') {
return currentData.gcp;
}
if (currentProvider === 'aws') {
return currentData.aws;
}
if (currentProvider === 'all') {
if (!currentData.gcp || !currentData.aws) return null;
const combined = {
summary: [...(currentData.gcp.summary || []), ...(currentData.aws.summary || [])]
.reduce((acc, service) => {
const cost = service.daily_cost ?? service.total_cost ?? service.cost ?? 0;
const existing = acc.find(s => s.service === service.service);
if (existing) {
existing.total_cost = (existing.total_cost || 0) + cost;
} else {
acc.push({ ...service, total_cost: cost });
}
return acc;
}, [])
.sort((a, b) => {
const costA = a.daily_cost ?? a.total_cost ?? a.cost ?? 0;
const costB = b.daily_cost ?? b.total_cost ?? b.cost ?? 0;
return costB - costA;
}),
forecast: {
month_to_date_cost: (currentData.gcp.forecast?.month_to_date_cost || 0) + (currentData.aws.forecast?.month_to_date_cost || 0),
daily_average_last_7_days: (currentData.gcp.forecast?.daily_average_last_7_days || 0) + (currentData.aws.forecast?.daily_average_last_7_days || 0),
total_monthly_forecast: (currentData.gcp.forecast?.total_monthly_forecast || 0) + (currentData.aws.forecast?.total_monthly_forecast || 0),
remaining_days_in_month: currentData.gcp.forecast?.remaining_days_in_month
},
anomalies: [...(currentData.gcp.anomalies || []), ...(currentData.aws.anomalies || [])],
history: currentData.gcp.history
};
return combined;
}
return null;
// AWS er parkert; all visning baseres på GCP-data
return currentData.gcp;
}
@ -640,7 +611,6 @@ function calculateDailyCosts(history) {
const dailyCosts = [];
const labels = [];
let lastMtd = 0;
for (let i = 0; i < history.length; i++) {
const entry = history[i];
@ -828,6 +798,7 @@ function exportPDF() {
window.print();
}
document.addEventListener('DOMContentLoaded', checkAuth);
if ('serviceWorker' in navigator) {
@ -846,7 +817,7 @@ if ('serviceWorker' in navigator) {
border: none;
color: var(--muted);
padding: 4px 10px;
cursor: pointer;
cursor: default;
border-radius: 4px;
font-size: 12px;
font-family: var(--font);