Release: security and quality fixes #14

Merged
eliott merged 9 commits from develop into main 2026-05-12 22:19:53 +02:00
Owner

Promotes the security & quality fixes from develop to main.

Includes mobile PR #13 (merged): typed ApiResult envelope, robust auth, deep-link gating, secure password inputs, claim-shape alignment with new API contract.

7 underlying commits, +757 / -403 across 34 files. pnpm tsc --noEmit clean. No breaking schema changes (mobile app).

Promotes the security & quality fixes from develop to main. Includes mobile PR #13 (merged): typed ApiResult envelope, robust auth, deep-link gating, secure password inputs, claim-shape alignment with new API contract. 7 underlying commits, +757 / -403 across 34 files. pnpm tsc --noEmit clean. No breaking schema changes (mobile app).
- Refactor hooks/api.tsx to return ApiResult<T> instead of ApiResponse<T> | undefined
- Move fetch implementation out of the React closure; memoize apiFetch with useCallback
- Parse error body via body.data?.message ?? body.message ?? statusText
- Only set Content-Type: application/json when there is a body and it's not FormData
- Add an idempotent 401 handler at the hook level: clear SecureStore + redirect to login
- AuthContext: defensive parseJwt for new + old JWT claim shapes, wipe partial stored state, expired-token guard, robust logout that always clears local state, status-aware French error messages on login, drop rememberMe (no refresh-token flow on mobile)
- Update PASSWORD_PATTERN to match new API rule (>=12 chars, upper/lower/digit/symbol)
- Fix types/auth.ts parameter syntax error

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Login: try/catch + setLoading(false) in finally; the AuthContext renders status-aware toasts (401 invalid creds, 403 disabled, 403 email-not-verified)
- Login: drop password complexity pattern from the login form (kept on register + change-password)
- Login: drop the tautological "value !== undefined || value !== ''" branch — always show the "obligatoire" message
- Login: hide rememberMe (no refresh-token flow on mobile)
- Add secureTextEntry, autoComplete, textContentType, autoCapitalize=none, autoCorrect=false on every password and username field (login, register)
- Update French password rule text in register to match the new server rule

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/(tabs)/account: render-gate on !authLoading && isAuthenticated; auto-logout(false) on 401 from /user; disable delete + logout buttons during their async work; remove duplicated lastName row
- app/pages/account/_layout: new Stack with <Redirect href="/(tabs)/login"/> guard so deep links can't bypass auth
- AccountMenu: drop @ts-ignore, type as Href from expo-router
- UpdateUserEmail/UpdateUserInfo/UpdateUserPassword: migrate to ApiResult envelope (res.ok), wrap in try/finally
- UpdateUserPassword: separate showOldPassword/showNewPassword toggles, secureTextEntry + autoComplete current-password/new-password + textContentType, French error text reflecting the new ≥12-char complexity rule

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace useRoute<any>() with typed useLocalSearchParams<{...}>() from expo-router
- Migrate the fetch sites to res.ok/res.data shape
- exercice-respiration: drop the throw on no-data; just return — toast already surfaced by apiFetch

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- InformationPageCard / RelaxationActivityCard: stop mutating props in place; introduce local isFavorite state with optimistic UI and rollback on failure; disable favorite button during in-flight request
- InformationPageList / RelaxationActivityList / home index: wrap empty-state condition in parens — "!x || x.totalCount === 0 && (...)" was misbinding because && has higher precedence than ||
- Migrate to res.ok envelope

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- TrackerModal.deleteTracker: await + close modal + call onUpdate on success; only close on res.ok; toast is rendered via apiFetch on error
- TrackerModal + tracker page: parse YYYY-MM-DD via dayjs(date).toDate() instead of new Date('YYYY-MM-DD'), which is interpreted as UTC midnight and can shift to the previous local day
- RespirationExerciseCard: disabled={deleting} during delete, only refresh on res.ok
- RespirationExerciseModal: bail on res.ok=false; setIsLoading(false) in finally
- StressDiagnosticForm: type updateAnswer value as number|boolean; replace dead "occurences === undefined || null" check with a real constraint ("at least one positive answer")
- Migrate remaining call sites (RespirationExerciseList, StressDiagnosticList, StressDiagnosticResult, EmotionSelect, diagnostic tab) to res.ok envelope

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catches the dep-array bug class — useEffect dependencies, missed
useCallback wrappers, and similar hook-lifecycle issues that show up
as runaway refetch loops at runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reviewed-on: #13
# Conflicts:
#	app/(tabs)/account.tsx
#	components/user/UpdateUserEmail.tsx
#	components/user/UpdateUserPassword.tsx
#	context/AuthContext.tsx
#	hooks/api.tsx
eliott merged commit ec2452c275 into main 2026-05-12 22:19:53 +02:00
Commenting is not possible because the repository is archived.
No reviewers
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
bloc-cesi/cesizen-mobile!14
No description provided.