Some checks failed
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Failing after 2s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m19s
- Added new functionality for exporting and importing matrix editor data in JSON and CSV formats within the MaturityMatrixToolsAdmin component. - Updated the API utility functions to support matrix editor exports and imports, enhancing the backend communication for Superadmin tasks. - Refactored the client API to streamline request handling and improve code clarity. - Included new UI elements for file upload and download actions, improving user experience in managing matrix data.
96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
/**
|
|
* HTTP-Client: Token, Mandanten-Header, Fehler-Mapping.
|
|
* Alle API-Aufrufe laufen über request() — siehe utils/api.js (Facade) und Domänenmodule (planning.js, exercises.js).
|
|
*/
|
|
|
|
export const API_URL = import.meta.env.VITE_API_URL || ''
|
|
|
|
/** LocalStorage + Request-Header für Mandanten-Kontext */
|
|
export const ACTIVE_CLUB_STORAGE_KEY = 'shinkan_active_club_id'
|
|
|
|
export function mergeActiveClubHeader(headers = {}) {
|
|
const cid = localStorage.getItem(ACTIVE_CLUB_STORAGE_KEY)
|
|
if (cid && /^\d+$/.test(String(cid).trim())) {
|
|
return { ...headers, 'X-Active-Club-Id': String(cid).trim() }
|
|
}
|
|
return headers
|
|
}
|
|
|
|
async function _fetchWithAuth(endpoint, options = {}) {
|
|
const token = localStorage.getItem('authToken')
|
|
const method = (options.method || 'GET').toUpperCase()
|
|
|
|
const headers = mergeActiveClubHeader({
|
|
...options.headers,
|
|
})
|
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
if (!headers['Content-Type'] && !headers['content-type']) {
|
|
headers['Content-Type'] = 'application/json'
|
|
}
|
|
}
|
|
|
|
if (token) {
|
|
headers['X-Auth-Token'] = token
|
|
}
|
|
|
|
const url = `${API_URL}${endpoint}`
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers,
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text()
|
|
let parsed = null
|
|
try {
|
|
parsed = JSON.parse(text)
|
|
} catch {
|
|
parsed = null
|
|
}
|
|
if (parsed?.detail != null) {
|
|
const d = parsed.detail
|
|
throw new Error(typeof d === 'string' ? d : JSON.stringify(d))
|
|
}
|
|
if (response.status === 502) {
|
|
throw new Error(
|
|
'HTTP 502 (Bad Gateway): Der Reverse-Proxy hat die API nicht korrekt erreicht. Ist `shinkan-api` aktiv (`docker compose ps`, `docker logs shinkan-api`)? Bei Host-Routing nur einen Weg verwenden — alles auf Port 3003 (Nginx nach `backend:8000`) oder sauber `/api` → Backend-Port.'
|
|
)
|
|
}
|
|
const snippet = (text || '').replace(/\s+/g, ' ').trim().slice(0, 180)
|
|
throw new Error(snippet ? `HTTP ${response.status}: ${snippet}` : `HTTP ${response.status}`)
|
|
}
|
|
|
|
return response
|
|
} catch (e) {
|
|
if (e instanceof TypeError && (e.message === 'Failed to fetch' || e.message.includes('fetch'))) {
|
|
const hint =
|
|
API_URL && API_URL.length > 0
|
|
? `Verbindung zum API unter ${API_URL} fehlgeschlagen. Läuft das Backend (z. B. Port 8098) und ist CORS erlaubt?`
|
|
: 'Kein VITE_API_URL gesetzt: Anfragen gehen an die Frontend-URL und schlagen oft fehl. Setze in .env z. B. VITE_API_URL=http://localhost:8098 und starte Vite neu.'
|
|
throw new Error(`${hint} [Technisch: ${e.message}; URL war ${endpoint}]`)
|
|
}
|
|
throw e
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generischer API-Aufruf inkl. X-Auth-Token und X-Active-Club-Id.
|
|
*/
|
|
export async function request(endpoint, options = {}) {
|
|
const response = await _fetchWithAuth(endpoint, options)
|
|
return response.json()
|
|
}
|
|
|
|
/** Text-Download (z. B. CSV-Export) mit gleicher Auth wie request(). */
|
|
export async function requestText(endpoint, options = {}) {
|
|
const response = await _fetchWithAuth(endpoint, options)
|
|
const disposition = response.headers.get('Content-Disposition') || ''
|
|
const match = disposition.match(/filename="([^"]+)"/)
|
|
return {
|
|
text: await response.text(),
|
|
filename: match ? match[1] : null,
|
|
}
|
|
}
|