chore(version): update version and changelog for release 0.8.133
All checks were successful
Deploy Development / deploy (push) Successful in 38s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m21s

- Bumped APP_VERSION to 0.8.133 and updated the changelog to reflect recent changes.
- Initiated Frontend Phase 4 Welle 1, introducing a centralized HTTP client in `frontend/src/api/client.js` while maintaining `utils/api.js` as a facade.
- Documented the changes in the architecture roadmap and README for clarity on the new API structure.
This commit is contained in:
Lars 2026-05-14 21:31:04 +02:00
parent e7dc6a6cd3
commit 8f5af49a6f
5 changed files with 98 additions and 83 deletions

View File

@ -1,6 +1,6 @@
# Shinkan Jinkendo Version Information
APP_VERSION = "0.8.132"
APP_VERSION = "0.8.133"
BUILD_DATE = "2026-05-12"
DB_SCHEMA_VERSION = "20260514062"
@ -36,6 +36,13 @@ MODULE_VERSIONS = {
}
CHANGELOG = [
{
"version": "0.8.133",
"date": "2026-05-14",
"changes": [
"Frontend Phase 4 Welle 1: frontend/src/api/client.js (request, ACTIVE_CLUB_STORAGE_KEY); utils/api.js importiert Client, bleibt Facade. Roadmap Phase 4 gestartet.",
],
},
{
"version": "0.8.132",
"date": "2026-05-14",

View File

@ -9,6 +9,7 @@ Dieses Bündel ist die **Leitlinie für die große Refaktorierung** nach dem MVP
| [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md) | Zielarchitektur (Frontend, API, Daten), Qualitätsziele, Einbindung neuer Features |
| [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md) | Erfasste Architekturschuld, Reihenfolge und Massnahmen zur Behebung |
| [UMSETZUNGSPLAN_ROADMAP.md](./UMSETZUNGSPLAN_ROADMAP.md) | Phasen, Meilensteine, Abnahmekriterien, Aufwandsschwerpunkte |
| [`frontend/src/api/client.js`](../../frontend/src/api/client.js) | Phase 4: zentraler HTTP-Client (`request`); `utils/api.js` bleibt Facade |
| [BASELINE_SNAPSHOT.md](./BASELINE_SNAPSHOT.md) | Phase 0: Bundle-, API- und Last-Baseline (Messvorlagen, Vergleich nach Phase 2) |
| [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md) | **Verbindliche** Shinkan-spezifische Regeln (Ergänzung zu den globalen Rules) |

View File

@ -8,6 +8,7 @@
- **Phase 2:** **abgeschlossen** (2026-05-14) — Indizes 058062, Keyset `/api/exercises` + `/api/training-units`, **`/api/dashboard/kpis`** inkl. `training_home`, EXPLAIN-Vorlagen **`scripts/load/explain-readpaths.sql`**.
- **Offen Phase 1:** Inbox optional **TTL** / nur bei sichtbarem Widget.
- **Phase 3 (abgeschlossen 2026-05-14):** Übungsliste modularisiert; Trainingsplanung/Übungsformular: **Page-Dateien unter Soft-Limit** — Implementierung in `TrainingPlanningPageRoot.jsx`, `ExerciseFormPageRoot.jsx`, `ExercisesListPageRoot.jsx`; `pages/*.jsx` nur Re-Export. Playwright **Tests 910**.
- **Phase 4 (gestartet 2026-05-14):** API **Welle 1**`frontend/src/api/client.js` (`request`); `utils/api.js` bleibt Facade.
**Ziel:** Nach MVP eine **nachhaltige** Architektur für Wachstum, **Performance** (Server + schwache Clients) und **sichere Feature-Erweiterung**.
**Leitdokumente:** [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md), [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md), [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md).
@ -92,13 +93,15 @@
## Phase 4 API-Client Modularisierung
**Status:** **gestartet** (2026-05-14) — Welle 1: **`frontend/src/api/client.js`** (`request`, `ACTIVE_CLUB_STORAGE_KEY`); **`utils/api.js`** bleibt vollständige Facade (bestehende Importe unverändert).
**Fokus:** Wartbarkeit für viele neue Features.
| Task | Bezug |
|------|--------|
| `frontend/src/api/` anlegen, `request`/`client` zentral | A2 |
| Facade: bestehende Importe von `utils/api` nicht sofort alle brechen; Migration in Wellen | A2 |
| Neue Endpoints nur noch in Domänen-Dateien | S3 |
| Task | Bezug | Status |
|------|-------|--------|
| `frontend/src/api/client.js` zentraler HTTP-Client | A2 | erledigt (Welle 1) |
| Domänen-Module unter `frontend/src/api/` + schrittweise Entlastung von `api.js` | A2 | offen |
| Neue Endpoints primär in Domänen-Dateien | S3 | offen |
**Abnahme:** Anteil neuer Module > X% der neuen Zeilen (Team-Ziel); Monolith wächst nicht weiter.

View File

@ -0,0 +1,79 @@
/**
* HTTP-Client: Token, Mandanten-Header, Fehler-Mapping.
* Alle API-Aufrufe laufen über request() siehe utils/api.js (Facade) und künftige Domänenmodule.
*/
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'
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 }
}
/**
* Generischer API-Aufruf inkl. X-Auth-Token und X-Active-Club-Id.
*/
export async function request(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.json()
} 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
}
}

View File

@ -6,84 +6,9 @@
import { stripHtmlToText } from './htmlUtils'
import { normalizeAdvanceMode, parseComboRepSeriesCountUi } from './combinationMethodProfileUi'
import { request, ACTIVE_CLUB_STORAGE_KEY } from '../api/client.js'
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'
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 }
}
/**
* Generic API request with automatic token injection
*/
async function request(endpoint, options = {}) {
const token = localStorage.getItem('authToken')
const method = (options.method || 'GET').toUpperCase()
const headers = mergeActiveClubHeader({
...options.headers,
})
// GET ohne Body: kein Content-Type: application/json (manche Proxies/Headers stören sich)
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.json()
} 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.'
// Ursache oft: CORS (v. a. bei Fehler-Antworten ohne CORS-Header), Adblocker, falsche URL —
// Login kann trotzdem klappen wenn nur eine andere Route betroffen ist.
throw new Error(`${hint} [Technisch: ${e.message}; URL war ${endpoint}]`)
}
throw e
}
}
export { ACTIVE_CLUB_STORAGE_KEY }
// ============================================================================
// Auth