Fix Init Admin : Séparation page HTML et endpoint API
Correction de l’erreur Init Admin : Séparation page HTML et endpoint API
Section titled “Correction de l’erreur Init Admin : Séparation page HTML et endpoint API”1. Description du Problème
Section titled “1. Description du Problème”Lors de la création d’un utilisateur administrateur via l’URL https://amn-dev.anderson-metsanou.workers.dev/api/auth/init-admin, le client recevait une erreur “Erreur réseau ou serveur indisponible” alors que les logs Cloudflare montraient un statut 200 OK.
Logs Cloudflare :
Section titled “Logs Cloudflare :”{ "statusCode": 200, "outcome": "ok", "path": "/api/auth/init-admin"}Erreur client :
Section titled “Erreur client :”Erreur réseau ou serveur indisponibleTest local curl :
Section titled “Test local curl :”curl -X POST http://localhost:8787/api/auth/init-admin \ -H "Content-Type: application/json" \ -d '{"email":"test@test.com","password":"password123","name":"Test Admin"}'Résultat : Retourne du HTML au lieu du JSON
2. Analyse de la Cause Racine
Section titled “2. Analyse de la Cause Racine”-
Fichier unique mélangeant HTML et API :
src/pages/api/auth/init-admin.astroétait un fichier Astro qui rendait du HTML- Quand on faisait un POST, Astro continuait à rendre la page HTML au lieu de traiter la requête API
-
Content-Type incorrect :
- La réponse avait
Content-Type: text/htmlau lieu deapplication/json - Le client ne pouvait pas parser le HTML comme JSON
- La réponse avait
-
Structure de fichier Astro :
- Les fichiers
.astrodanssrc/pages/api/auth/sont traités comme des pages - Ils rendent toujours du HTML, même pour les requêtes POST
- Les fichiers
3. Solution Appliquée
Section titled “3. Solution Appliquée”Séparation des responsabilités :
Section titled “Séparation des responsabilités :”-
Déplacer la page HTML :
- Déplacer
src/pages/api/auth/init-admin.astroverssrc/pages/init-admin.astro - Cette page servira uniquement l’interface utilisateur à
/init-admin
- Déplacer
-
Créer l’endpoint API dédié :
- Créer
src/pages/api/auth/init-admin.tspour gérer les requêtes POST - Ce fichier retourne uniquement du JSON
- Créer
Nouvelle structure :
Section titled “Nouvelle structure :”Page HTML (src/pages/init-admin.astro) :
- Rend l’interface utilisateur pour créer un admin
- Accessible via
/init-admin - Contient le formulaire et le script client
Endpoint API (src/pages/api/auth/init-admin.ts) :
- Gère les requêtes POST à
/api/auth/init-admin - Retourne du JSON avec la réponse Better Auth
- Contient uniquement la logique serveur
Code de l’endpoint API (src/pages/api/auth/init-admin.ts) :
Section titled “Code de l’endpoint API (src/pages/api/auth/init-admin.ts) :”import type { APIRoute } from 'astro';import { createAuth } from '../../../auth/index';
export const prerender = false;
async function getEnv() { try { const { env } = await import('cloudflare:workers'); return env; } catch (_e) { return null; }}
export const POST: APIRoute = async (context) => { try { const env = await getEnv();
if (!env) { return new Response(JSON.stringify({ error: 'Cloudflare environment not available' }), { status: 500, headers: { 'Content-Type': 'application/json' } }); }
const body = await context.request.json(); const { email, password, name } = body as { email?: string; password?: string; name?: string };
if (!email || !password) { return new Response(JSON.stringify({ error: 'Email and password are required' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); }
const auth = createAuth(env);
const result = await auth.api.signUpEmail({ body: { email, password, name: name || email.split('@')[0] } });
return new Response(JSON.stringify({ success: true, user: result }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (err) { console.error('[Init admin error]', err); return new Response(JSON.stringify({ error: 'Internal server error', message: err instanceof Error ? err.message : String(err) }), { status: 500, headers: { 'Content-Type': 'application/json' } }); }};Correction des imports dans la page HTML :
Section titled “Correction des imports dans la page HTML :”Avant :
---import BaseHead from '../../../components/BaseHead.astro';import Header from '../../../components/Header.astro';import Footer from '../../../components/Footer.astro';---Après :
---import BaseHead from '../components/BaseHead.astro';import Header from '../components/Header.astro';import Footer from '../components/Footer.astro';---4. Références Astro
Section titled “4. Références Astro”Selon la documentation Astro sur le routing :
- Les fichiers dans
src/pages/avec l’extension.astrosont des pages qui rendent du HTML - Les fichiers API doivent avoir l’extension
.tsou.jspour retourner du JSON - Les fichiers dans
src/pages/api/avec.astrosont toujours traités comme des pages HTML
Structure correcte pour les APIs :
Section titled “Structure correcte pour les APIs :”- Pages HTML :
src/pages/nom-page.astro→ rend HTML - Endpoints API :
src/pages/api/nom-endpoint.ts→ retourne JSON
5. Tests de Validation
Section titled “5. Tests de Validation”Test après correction :
Section titled “Test après correction :”- Build local : ✅ Succès
- Test endpoint API : À tester avec curl
- Déploiement : À tester via GitHub Actions
- Création admin : À tester sur l’URL déployée
Test curl attendu :
Section titled “Test curl attendu :”curl -X POST http://localhost:8787/api/auth/init-admin \ -H "Content-Type: application/json" \ -d '{"email":"test@test.com","password":"password123","name":"Test Admin"}'Résultat attendu :
{ "success": true, "user": { "email": "test@test.com", "name": "Test Admin", "id": "..." }}6. Leçons Apprises
Section titled “6. Leçons Apprises”-
Séparation des responsabilités :
- Les pages HTML et les endpoints API doivent être dans des fichiers séparés
- Les fichiers
.astrodanssrc/pages/api/sont traités comme des pages HTML
-
Structure de fichiers Astro :
- Les fichiers API doivent avoir l’extension
.tsou.js - Les fichiers
.astrosont réservés au rendu HTML
- Les fichiers API doivent avoir l’extension
-
Tests d’API :
- Toujours tester les endpoints avec curl avant de les utiliser dans le frontend
- Vérifier le Content-Type de la réponse