Skip to content

Fix Init Admin : Séparation page HTML et endpoint API

Fix Init Admin : Séparation page HTML et endpoint API

Documentation de la résolution de l'erreur réseau lors de la création d'un admin via l'endpoint init-admin.

Correction de l’erreur Init Admin : Séparation page HTML et endpoint API

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 :

{
"statusCode": 200,
"outcome": "ok",
"path": "/api/auth/init-admin"
}

Erreur client :

Erreur réseau ou serveur indisponible

Test local curl :

Terminal window
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 Racine1/2

  1. 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

2. Analyse de la Cause Racine2/2

  1. Content-Type incorrect :

    • La réponse avait Content-Type: text/html au lieu de application/json
    • Le client ne pouvait pas parser le HTML comme JSON
  2. Structure de fichier Astro :

    • Les fichiers .astro dans src/pages/api/auth/ sont traités comme des pages
    • Ils rendent toujours du HTML, même pour les requêtes POST

3. Solution Appliquée

Séparation des responsabilités :

  1. Déplacer la page HTML :

    • Déplacer src/pages/api/auth/init-admin.astro vers src/pages/init-admin.astro
    • Cette page servira uniquement l’interface utilisateur à /init-admin
  2. Créer l’endpoint API dédié :

    • Créer src/pages/api/auth/init-admin.ts pour gérer les requêtes POST
    • Ce fichier retourne uniquement du JSON

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) :1/2

Code de l’endpoint API (src/pages/api/auth/init-admin.ts) :2/2

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 :1/2

Avant :

---
import BaseHead from '../../../components/BaseHead.astro';
import Header from '../../../components/Header.astro';
import Footer from '../../../components/Footer.astro';
---

Correction des imports dans la page HTML :2/2

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

Selon la documentation Astro sur le routing :

  • Les fichiers dans src/pages/ avec l’extension .astro sont des pages qui rendent du HTML
  • Les fichiers API doivent avoir l’extension .ts ou .js pour retourner du JSON
  • Les fichiers dans src/pages/api/ avec .astro sont toujours traités comme des pages HTML

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

Test après correction :

  1. Build local : ✅ Succès
  2. Test endpoint API : À tester avec curl
  3. Déploiement : À tester via GitHub Actions
  4. Création admin : À tester sur l’URL déployée

Test curl attendu :1/2

Terminal window
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"}'

Test curl attendu :2/2

Résultat attendu :

{
"success": true,
"user": {
"email": "test@test.com",
"name": "Test Admin",
"id": "..."
}
}

6. Leçons Apprises1/2

  1. Séparation des responsabilités :

    • Les pages HTML et les endpoints API doivent être dans des fichiers séparés
    • Les fichiers .astro dans src/pages/api/ sont traités comme des pages HTML

6. Leçons Apprises2/2

  1. Structure de fichiers Astro :

    • Les fichiers API doivent avoir l’extension .ts ou .js
    • Les fichiers .astro sont réservés au rendu HTML
  2. 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
Switch to normal mode

Contents