Professeur TOP
+Connexion eleve, professeur ou maintenance.
+{status}
} +diff --git a/MEMORY.md b/MEMORY.md
index 0e862bd..d5b2d5d 100644
--- a/MEMORY.md
+++ b/MEMORY.md
@@ -17,6 +17,8 @@ Ce projet est un POC de professeur virtuel pour enfants, avec:
- Le backend a un socle d'authentification avec comptes `users`, roles `student`, `teacher`, `maintenance`.
- Le compte admin/prof initial est seede depuis `.env` via `ADMIN_USERNAME` et `ADMIN_PASSWORD`.
- Le login pose un cookie HTTP-only `professeur_top_session` et renvoie aussi un token bearer.
+- Le frontend affiche maintenant une page de connexion avant l'application et verifie la session via `/auth/me`.
+- Apres connexion admin/prof, l'interface actuelle reste accessible avec un bouton de deconnexion.
- Le vrai `docker-compose.yml` de production n'est pas dans ce repo. Il est situe un niveau au-dessus sur le serveur.
- La conf nginx reelle route:
- `/api/` vers `tutor-backend:8000`
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 91b0685..bcc6211 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -30,13 +30,19 @@ async function parseApiResponse(res) {
return data
}
-async function apiFetch(path, options) {
- const res = await fetch(`${API_BASE}${path}`, options)
+async function apiFetch(path, options = {}) {
+ const res = await fetch(`${API_BASE}${path}`, {
+ credentials: 'same-origin',
+ ...options,
+ })
return parseApiResponse(res)
}
async function fetchAudio(path, options) {
- const res = await fetch(`${API_BASE}${path}`, options)
+ const res = await fetch(`${API_BASE}${path}`, {
+ credentials: 'same-origin',
+ ...options,
+ })
if (!res.ok) {
let detail = `Erreur API (${res.status})`
const contentType = res.headers.get('content-type') || ''
@@ -99,7 +105,72 @@ function getSupportedRecordingMimeType() {
return candidates.find((type) => MediaRecorder.isTypeSupported(type)) || ''
}
-export default function App() {
+function LoginPage({ onLogin }) {
+ const [credentials, setCredentials] = useState({ username: '', password: '' })
+ const [status, setStatus] = useState('')
+ const [isSubmitting, setIsSubmitting] = useState(false)
+
+ async function submitLogin(e) {
+ e.preventDefault()
+ if (!credentials.username.trim() || !credentials.password) return
+
+ setIsSubmitting(true)
+ setStatus('')
+ try {
+ const data = await apiFetch('/auth/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ username: credentials.username.trim(),
+ password: credentials.password,
+ }),
+ })
+ onLogin(data.user)
+ } catch (error) {
+ setStatus(error.message || 'Connexion impossible.')
+ } finally {
+ setIsSubmitting(false)
+ }
+ }
+
+ return (
+ Connexion eleve, professeur ou maintenance. {status}Professeur TOP
+
Verification de la session...
+