addition of new dashboard
This commit is contained in:
100
backend/http/dashboardPassthrough.js
Normal file
100
backend/http/dashboardPassthrough.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import { service as defaultService } from "../modules/dashboard/service.js";
|
||||
import { getConfig } from "../common/config.js";
|
||||
|
||||
// Same interface as the local service, so it is a drop-in for it.
|
||||
//
|
||||
// Tenant scoping: locally, every call carries the session's own user id.
|
||||
// Remotely, the API KEY is the scope — we deliberately send NO user id, since a
|
||||
// local primary key is meaningless on another operator and must never be
|
||||
// trusted as a cross-operator identity.
|
||||
export function makePassthrough({ service = defaultService, config = getConfig(), fetchImpl = fetch } = {}) {
|
||||
const sources = () => config.dashboard || { core: {}, audit: {} };
|
||||
const remote = (s) => sources()[s] && sources()[s].url;
|
||||
const federated = () => Boolean(remote("core") || remote("audit"));
|
||||
|
||||
async function fromSource(source, endpoint, params, localCall) {
|
||||
const src = sources()[source];
|
||||
if (!src || !src.url) return localCall();
|
||||
const res = await fetchImpl(`${src.url}/api/v1/data/dashboard/${endpoint}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", Authorization: `Bearer ${src.key}` },
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
if (!res.ok) throw new Error(`federation: ${source}/${endpoint} -> ${res.status}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
const num = (v) => Number(v) || 0;
|
||||
|
||||
function mergeSummary(core, audit) {
|
||||
const cu = core.apiUsage || {}, au = audit.apiUsage || {};
|
||||
const cs = core.storage || {}, as = audit.storage || {};
|
||||
const split = (c, a) => ({ core: num(c), audit: num(a), total: num(c) + num(a) });
|
||||
return {
|
||||
apiUsage: split(cu.core, au.audit),
|
||||
apiKeys: core.apiKeys,
|
||||
endpoints: core.endpoints,
|
||||
storage: {
|
||||
onDisk: split(cs.onDisk?.core, as.onDisk?.audit),
|
||||
logical: split(cs.logical?.core, as.logical?.audit),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function mergeByDate(coreSeries = [], auditSeries = [], coreKey, auditKey, totalKey) {
|
||||
const auditByDate = new Map(auditSeries.map((r) => [r.date, r]));
|
||||
return coreSeries.map((r) => {
|
||||
const a = auditByDate.get(r.date) || {};
|
||||
const core = num(r[coreKey]);
|
||||
const audit = num(a[auditKey]);
|
||||
return { date: r.date, [coreKey]: core, [auditKey]: audit, [totalKey]: core + audit };
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
async summary(userId, window) {
|
||||
if (!federated()) return service.summary(userId, window);
|
||||
const [core, audit] = await Promise.all([
|
||||
fromSource("core", "summary", window, () => service.summary(userId, window)),
|
||||
fromSource("audit", "summary", window, () => service.summary(userId, window)),
|
||||
]);
|
||||
return mergeSummary(core, audit);
|
||||
},
|
||||
|
||||
async transactions(userId, opts) {
|
||||
const source = opts.type === "audit" ? "audit" : "core";
|
||||
return fromSource(source, "transactions", opts, () => service.transactions(userId, opts));
|
||||
},
|
||||
|
||||
async series(userId, window) {
|
||||
if (!federated()) return service.series(userId, window);
|
||||
const [core, audit] = await Promise.all([
|
||||
fromSource("core", "series", window, () => service.series(userId, window)),
|
||||
fromSource("audit", "series", window, () => service.series(userId, window)),
|
||||
]);
|
||||
return {
|
||||
usage: mergeByDate(core.usage, audit.usage, "core", "audit", "total"),
|
||||
storage: mergeByDate(core.storage, audit.storage, "coreDisk", "auditDisk", "totalDisk"),
|
||||
};
|
||||
},
|
||||
|
||||
// Audit ids belong to the audit source that served the listing.
|
||||
async verify(userId, targets) {
|
||||
const { eventUuids = [], auditIds = [] } = targets || {};
|
||||
const params = { ids: eventUuids.join(","), auditIds: auditIds.join(",") };
|
||||
return fromSource("audit", "verify", params, () => service.verify(userId, targets));
|
||||
},
|
||||
|
||||
async event(userId, eventUuid) {
|
||||
return fromSource("core", "event", { eventUuid }, () => service.event(userId, eventUuid));
|
||||
},
|
||||
|
||||
async agreement(userId, agreementUuid) {
|
||||
return fromSource("core", "agreement", { agreementUuid }, () => service.agreement(userId, agreementUuid));
|
||||
},
|
||||
|
||||
async details(userId, eventUuid) {
|
||||
return fromSource("core", "details", { eventUuid }, () => service.details(userId, eventUuid))
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user