CG3: park AWS billing UI and make history endpoint best-effort so GCP dashboard stays live
This commit is contained in:
parent
88037bdba2
commit
868a0e496d
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user