Files
boc/landvex-finance-sprint3/js/api.mjs
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

180 lines
4.8 KiB
JavaScript

/**
* LandveX Finance — API Layer
* Sprint 3: UI-refaktorering
*
* Centraliserad fetch-wrapper med auth, timeout, retry och felhantering.
*/
const API_BASE = '';
const TENANT = 'landvex';
const DEFAULT_TIMEOUT = 10000;
const MAX_RETRIES = 1;
// ── Helpers ──
function getToken() {
// Försök hämta JWT från cookie eller localStorage
const match = document.cookie.match(/wavult_token=([^;]+)/);
if (match) return decodeURIComponent(match[1]);
try {
return localStorage.getItem('wavult:token');
} catch { return null; }
}
function buildHeaders() {
const h = {
'x-internal-source': 'ouroboros-system',
'x-tenant-id': TENANT,
};
const tok = getToken();
if (tok) h['Authorization'] = `Bearer ${tok}`;
return h;
}
// ── Core fetch ──
export async function api(path, method = 'GET', body = null, opts = {}) {
const url = path.startsWith('http') ? path : `${API_BASE}${path}`;
const timeout = opts.timeout || DEFAULT_TIMEOUT;
const retries = opts.retries ?? MAX_RETRIES;
const headers = { ...buildHeaders() };
if (body && method !== 'GET') {
headers['Content-Type'] = 'application/json';
}
let lastError;
for (let attempt = 0; attempt <= retries; attempt++) {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), timeout);
try {
const options = {
method,
headers,
signal: ctrl.signal,
};
if (body && method !== 'GET') {
options.body = JSON.stringify(body);
}
const r = await fetch(url, options);
clearTimeout(t);
if (!r.ok) {
const err = new Error(`HTTP ${r.status}: ${r.statusText}`);
err.status = r.status;
err.code = `HTTP_${r.status}`;
throw err;
}
// Tomt svar (204)
if (r.status === 204) return null;
const contentType = r.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return await r.json();
}
return await r.text();
} catch (e) {
clearTimeout(t);
lastError = e;
// Retry vid nätverksfel eller 5xx
const shouldRetry = attempt < retries && (
e.name === 'AbortError' ||
e.name === 'TypeError' ||
(e.status >= 500 && e.status < 600)
);
if (!shouldRetry) break;
// Exponential backoff
await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
}
}
throw lastError;
}
// ── Convenience methods ──
export const get = (path, opts) => api(path, 'GET', null, opts);
export const post = (path, body, opts) => api(path, 'POST', body, opts);
export const put = (path, body, opts) => api(path, 'PUT', body, opts);
export const del = (path, opts) => api(path, 'DELETE', null, opts);
// ── Typed API wrappers ──
export async function getJournal(params = {}) {
const qs = new URLSearchParams(params).toString();
return get(`/api/ledger/journal?${qs}`);
}
export async function getTrialBalance(fiscalYear, period) {
const qs = new URLSearchParams({ fiscal_year: fiscalYear, period }).toString();
return get(`/api/ledger/trial-balance?${qs}`);
}
export async function getAccounts() {
return get('/api/ledger/accounts');
}
export async function getPeriods(fiscalYear) {
return get(`/api/ledger/periods?fiscal_year=${fiscalYear}`);
}
export async function getInvoices(fiscalYear) {
return get(`/api/ledger/invoices?fiscal_year=${fiscalYear}`);
}
export async function createJournalEntry(entry) {
return post('/api/ledger/journal', entry);
}
export async function postJournalEntry(id) {
return post(`/api/ledger/journal/${id}/post`, {});
}
export async function createInvoice(invoice) {
return post('/api/ledger/invoices', invoice);
}
export async function getTasks() {
return get('/api/ledger/tasks');
}
export async function uploadReceipt(entryId, file) {
const formData = new FormData();
formData.append('file', file);
const url = `/api/ledger/journal/${entryId}/upload`;
const headers = buildHeaders();
delete headers['Content-Type']; // Låt browser sätta boundary
const r = await fetch(url, {
method: 'POST',
headers,
body: formData,
});
if (!r.ok) throw new Error(`Upload failed: ${r.status}`);
return r.json();
}
export async function getReceipts(entryId) {
return get(`/api/ledger/journal/${entryId}/receipts`);
}
export async function exportSIE4(fiscalYear) {
return get(`/api/ledger/export/sie4?fiscal_year=${fiscalYear}`);
}
// ── Health ──
export async function getHealth() {
return get('/health');
}
export async function getFinanceHealth() {
return get('/health/finance');
}
export default { api, get, post, put, del, getJournal, getTrialBalance, getAccounts, getPeriods, getInvoices, createJournalEntry, postJournalEntry, createInvoice, getTasks, uploadReceipt, getReceipts, exportSIE4, getHealth, getFinanceHealth };