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
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:
parent
e7dc6a6cd3
commit
8f5af49a6f
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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) |
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
- **Phase 2:** **abgeschlossen** (2026-05-14) — Indizes 058–062, 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 9–10**.
|
||||
- **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.
|
||||
|
||||
|
|
|
|||
79
frontend/src/api/client.js
Normal file
79
frontend/src/api/client.js
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user