bharatgraph / frontend /js /api.js
abinazebinoy's picture
feat(phase-34): add 10 Phase 34 client methods to api.js
34cd641
Raw
History Blame Contribute Delete
5.63 kB
const API_BASE = window.BHARATGRAPH_API_URL || "http://localhost:8000";
const Api = {
_request: async (path, options = {}) => {
const url = `${API_BASE}${path}`;
try {
const response = await fetch(url, {
headers: { "Content-Type": "application/json", ...options.headers },
...options,
});
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: response.statusText }));
throw new Error(error.detail || `HTTP ${response.status}`);
}
return response.json();
} catch (err) {
// CodeQL #22 FIX: avoid template literal with user-controlled path
console.error("[API] request failed for path: " + String(path).substring(0, 100) + " -- " + String(err.message).substring(0, 200));
throw err;
}
},
health: () => Api._request("/health"),
stats: () => Api._request("/stats"),
search: (query, type = null, limit = 20, lang = "en") => {
const params = new URLSearchParams({ q: query, limit });
if (type) params.append("type", type);
if (lang && lang !== "en") params.append("lang", lang);
return Api._request(`/search?${params}`);
},
profile: (entityId) => Api._request(`/profile/${entityId}`),
risk: (entityId) => Api._request(`/risk/${entityId}`),
graphConnections: (entityId, depth = 2) =>
Api._request(`/graph/connections/${entityId}?depth=${depth}`),
politicianContracts: (limit = 50) =>
Api._request(`/graph/pattern/politician-contracts?limit=${limit}`),
multilingualSearch: (query, lang = "en") => {
const params = new URLSearchParams({ q: query, lang });
return Api._request(`/search/multilingual?${params}`);
},
riskMultilingual: (entityId, lang = "en") =>
Api._request(`/risk/multilingual/${entityId}?lang=${lang}`),
exportPdf: (entityId) => `${API_BASE}/export/pdf/${entityId}`,
verifyHash: (hash) => Api._request(`/verify/${hash}`),
nodeEvidence: (entityId) => Api._request(`/node-evidence/${entityId}`),
// Phase 33: timeline
timeline: function(entityId, category) {
var p = category ? "?category=" + encodeURIComponent(category) : "";
return Api._request("/timeline/" + entityId + p);
},
timelineByYear: function(entityId) {
return Api._request("/timeline/" + entityId + "/by-year");
},
// Phase 33: graph analytics
graphAnalytics: function(entityId, depth) {
var d = depth ? "?depth=" + depth : "";
return Api._request("/graph/analytics/" + entityId + d);
},
graphBetweenness: function(limit) {
var l = limit ? "?limit=" + limit : "";
return Api._request("/graph/centrality/betweenness" + l);
},
graphPagerank: function(limit) {
var l = limit ? "?limit=" + limit : "";
return Api._request("/graph/centrality/pagerank" + l);
},
graphCommunities: function(minSize) {
var m = minSize ? "?min_size=" + minSize : "";
return Api._request("/graph/communities" + m);
// Phase 34: forensic detection
forensicsCircularOwnership: function(maxLen) {
var p = maxLen ? "?max_cycle_length=" + maxLen : "";
return Api._request("/forensics/circular-ownership" + p);
},
forensicsGhostCompanies: function(minScore) {
var p = minScore ? "?min_score=" + minScore : "";
return Api._request("/forensics/ghost-companies" + p);
},
forensicsShadowDirectors: function(minCount) {
var p = minCount ? "?min_company_count=" + minCount : "";
return Api._request("/forensics/shadow-directors" + p);
},
forensicsBenfords: function(entityId) {
return Api._request("/forensics/benfords/" + entityId);
},
// Phase 34: self-learning
selfLearningPatterns: function() {
return Api._request("/self-learning/patterns");
},
selfLearningWeights: function() {
return Api._request("/self-learning/weights");
},
selfLearningAudit: function() {
return Api._request("/self-learning/audit");
},
// Phase 34: case memory
caseMemoryStats: function() {
return Api._request("/case-memory/stats");
},
caseMemorySimilar: function(findingTypes) {
var p = findingTypes ? "?finding_types=" + encodeURIComponent(findingTypes) : "";
return Api._request("/case-memory/similar" + p);
},
},
/**
* BUG-7 FIX: was always sending text as a URL query string on a POST,
* which caused 414 URI Too Long for text > ~2000 chars and silently
* truncated anything beyond the browser's URL limit.
*
* Fix: short text (< 400 chars) keeps the fast query-param path;
* long text is sent as a JSON body so there is no length limit.
*/
translate: (text, sourceLang = "en", targetLang = "hi") => {
const baseParams = new URLSearchParams({
source_lang: sourceLang,
target_lang: targetLang,
});
if (text.length < 400) {
// Short text: send via query string (original fast path)
baseParams.append("text", text);
return Api._request(`/translate?${baseParams}`, { method: "POST" });
}
// Long text: send as JSON body to avoid URL length limits
baseParams.append("text", ""); // keep param present but empty
return Api._request(`/translate?${baseParams}`, {
method: "POST",
body: JSON.stringify({ text }),
});
},
languages: () => Api._request("/languages"),
uiLabels: (lang = "en") => Api._request(`/ui-labels?lang=${lang}`),
createFeedSocket: () => {
// L-02 FIX: strip trailing slash before appending /ws/feed
// to avoid wss://example.com//ws/feed with double slash
const wsUrl = API_BASE.replace(/\/$/, "").replace(/^http/, "ws") + "/ws/feed";
return new WebSocket(wsUrl);
},
};
window.Api = Api;