⚠️ AVERTISSEMENT / DISCLAIMERCe projet est une DEMONSTRATION et n'est PAS destiné à un usage en production.
- Intentionnellement ouvert et permissif pour faciliter l'expérimentation
- Les fonctionnalités de sécurité sont minimales par conception
- Ne pas utiliser avec des tokens de production ou des données sensibles
Application React de démonstration pour l'API Halapi - un assistant conversationnel spécialisé dans les recommandations de livres et de musique.
- Chat en temps réel avec streaming SSE (Server-Sent Events)
- Recommandations de livres avec couvertures, auteurs, descriptions et sujets
- Recommandations musicales avec albums, pistes, pochettes et métadonnées complètes
- Aperçu des livres : cliquez sur une carte de livre pour voir sa présentation éditoriale
- Suggestions interactives sous forme de boutons pour continuer la conversation
- Gestion des tokens via UI : enregistrez votre token API depuis la page Settings
- Stockage sécurisé : tokens stockés côté serveur en SQLite, seul un hash est gardé côté client
- Proxy API : les requêtes passent par un serveur Hono qui injecte le vrai token
- Utilisateurs virtuels : simulation multi-utilisateurs avec
externalUserIdpour tester des scénarios multi-tenants - Indicateurs d'outils affichant le statut des appels d'outils en temps réel
- Rendu Markdown avec support GitHub Flavored Markdown (GFM)
- Historique des conversations filtré par utilisateur avec possibilité de reprendre une discussion
- Métadonnées détaillées : agent utilisé, modèle, temps d'exécution, tokens consommés
Le projet utilise le SDK halapi-js comme git submodule. Ce SDK fournit :
- Client API type-safe pour communiquer avec le backend Halapi
- Types TypeScript pour toutes les structures de données (messages, livres, musique, etc.)
- Méthode
getBookPresentations()pour récupérer les présentations éditoriales par ISBN - Utilitaires partagés (génération UUID, etc.)
example-halapi/
├── halapi-js/ # Submodule SDK (git submodule)
│ └── src/
│ ├── index.ts # Exports publics
│ ├── client.ts # Client API avec streaming
│ └── types.ts # Types TypeScript
├── server/ # Serveur Hono (proxy API + gestion tokens)
│ └── index.ts # Point d'entrée du serveur
├── src/
│ ├── config/
│ │ └── api.ts # Configuration API
│ ├── components/ # Composants React
│ ├── pages/
│ │ ├── ChatPage.tsx # Page principale de chat
│ │ ├── ConversationsPage.tsx # Liste des conversations
│ │ └── SettingsPage.tsx # Page d'enregistrement du token
│ └── ...
└── ...
L'architecture utilise un système de proxy pour sécuriser les tokens API :
- Enregistrement : L'utilisateur saisit son token API sur la page Settings
- Stockage serveur : Le token est stocké dans une base SQLite côté serveur
- Hash client : Un hash du token est retourné et stocké dans localStorage
- Requêtes proxy : Les appels API passent par le serveur Hono qui :
- Identifie l'utilisateur via le hash
- Récupère le vrai token depuis SQLite
- Injecte le token dans les requêtes vers l'API Halapi
Les utilisateurs virtuels sont stockés uniquement côté client (localStorage) :
halapi_virtual_users: Liste des utilisateurs crééshalapi_current_user: ID de l'utilisateur sélectionné
L'externalUserId de l'utilisateur courant est automatiquement inclus dans :
- Les requêtes de chat (pour créer des conversations liées à cet utilisateur)
- Les requêtes de liste des conversations (pour filtrer par utilisateur)
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │────▶│ Hono Server │────▶│ halapiClient│────▶│ API Halapi │
│ (hash only) │◀────│ (SQLite + │◀────│ (SDK) │◀────│ (SSE) │
│ │ │ real token) │ │ │ │ │
└─────────────┘ └──────────────┘ └─────────────┘ └─────────────┘
┌─────────────────┐ ┌──────────────┐ ┌─────────────┐
│ ChatPage │────▶│ useChat │────▶│ Hono Proxy │
│ │◀────│ hook │◀────│ + halapi │
└─────────────────┘ └──────────────┘ └─────────────┘
│ │ │
│ │ ▼
│ │ ┌─────────────┐
│ │ │ API Halapi │
│ │ │ (SSE) │
│ │ └─────────────┘
▼ ▼
┌─────────────────┐ ┌──────────────┐
│ ChatMessage │ │ Artifacts │
│ - Markdown │ │ - Books │
│ - ToolCalls │ │ - Music │
│ - Suggestions │ │ - Suggest. │
└─────────────────┘ └──────────────┘
- Node.js 20+
- npm ou pnpm
- Un token API Halapi (
hap_sk_live_...) - Git (pour cloner avec submodules)
# Cloner le repository avec les submodules
git clone --recurse-submodules https://github.com/darksip/example-api.git
cd example-api
# Si déjà cloné sans submodules
git submodule update --init --recursive
# Installer les dépendances
npm install# Mode développement avec hot reload
npm run devCette commande lance simultanément :
- Vite (port 5174) : serveur de développement frontend avec HMR
- Hono (port 3333) : serveur backend (proxy API + gestion tokens)
Accédez à l'application sur http://localhost:3333
Lors de la première visite, vous serez automatiquement redirigé vers la page Settings pour :
- Enregistrer votre token API Halapi
- Créer au moins un utilisateur virtuel (obligatoire)
Une fois configuré, vous pourrez accéder au chat. L'utilisateur courant est sélectionnable dans le header.
Les utilisateurs virtuels permettent de simuler des scénarios multi-tenants :
- Chaque utilisateur a son propre historique de conversations
- L'
externalUserIdest envoyé à l'API Halapi pour isoler les données - Changez d'utilisateur via le sélecteur dans le header
- La dernière conversation de l'utilisateur est automatiquement chargée
Note : L'unicité de l'externalUserId au sein de votre organisation est de votre responsabilité.
# Build de production
npm run build
# Prévisualisation du build
npm run previewLe projet inclut un fichier docker-compose.yaml pour un déploiement simple :
# Lancer l'application
docker compose up -dL'application sera accessible sur http://localhost:8080.
services:
app:
image: ghcr.io/darksip/example-api:main
ports:
- "8080:3333"
volumes:
- halapi-data:/app/data # Persistance SQLite
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3333/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
volumes:
halapi-data:| Variable | Description | Défaut |
|---|---|---|
VITE_HALAPI_URL |
URL de l'API Halapi | https://haldev.cybermeet.fr |
PORT |
Port du serveur Hono | 3333 |
Note : Le token API n'est plus passé en variable d'environnement. Il est enregistré via l'interface Settings et stocké dans SQLite.
Le projet utilise GitHub Actions pour builder et publier automatiquement l'image Docker sur GitHub Container Registry (ghcr.io).
Fichier : .github/workflows/docker-publish.yml
Déclencheurs :
- Push sur la branche
main - Push d'un tag
v*(ex:v1.0.0) - Pull requests vers
main(build sans push)
Étapes du workflow :
- Checkout avec submodules récursifs
- Setup Docker Buildx pour le cache multi-plateforme
- Login au GitHub Container Registry
- Build & Push de l'image avec cache GitHub Actions
Tags générés :
| Événement | Tags |
|---|---|
Push sur main |
ghcr.io/darksip/example-api:main |
Tag v1.2.3 |
ghcr.io/darksip/example-api:1.2.3, ghcr.io/darksip/example-api:1.2 |
| Pull Request #42 | ghcr.io/darksip/example-api:pr-42 (non publié) |
| Tout push | ghcr.io/darksip/example-api:<sha> |
Le workflow clone automatiquement les submodules grâce à l'option :
- uses: actions/checkout@v4
with:
submodules: recursiveCela garantit que halapi-js/ est disponible lors du build Docker.
| Script | Description |
|---|---|
npm run dev |
Lance Vite (5174) + Hono (3333) en parallèle |
npm run dev:client |
Serveur de développement Vite seul |
npm run dev:server |
Serveur Hono seul |
npm run build |
Vérification TypeScript + build production |
npm run preview |
Prévisualisation du build de production |
npm run lint |
Vérification du code avec Biome |
npm run lint:fix |
Correction automatique des erreurs de lint |
npm run format |
Formatage du code avec Biome |
npm run check |
Vérification complète Biome (lint + format) |
npm run check:fix |
Correction automatique complète |
npm run typecheck |
Vérification des types TypeScript |
example-halapi/
├── .github/
│ └── workflows/
│ └── docker-publish.yml # CI/CD GitHub Actions
├── halapi-js/ # Submodule SDK
├── server/
│ └── index.ts # Serveur Hono (proxy + SQLite)
├── src/
│ ├── components/
│ │ ├── BookCard.tsx # Carte de livre cliquable
│ │ ├── BookPreviewModal.tsx# Modal d'aperçu avec présentation
│ │ ├── ChatInput.tsx # Zone de saisie du chat
│ │ ├── ChatMessage.tsx # Message avec markdown et artifacts
│ │ ├── ConversationItem.tsx# Élément de liste des conversations
│ │ ├── Layout.tsx # Layout avec navigation + sélecteur utilisateur
│ │ └── MusicCard.tsx # Carte album ou piste musicale
│ ├── config/
│ │ └── api.ts # Configuration API + gestion utilisateurs virtuels
│ ├── hooks/
│ │ ├── useChat.ts # Gestion du chat avec streaming
│ │ └── useConversations.ts # Récupération des conversations
│ ├── pages/
│ │ ├── ChatPage.tsx # Page principale de chat
│ │ ├── ConversationsPage.tsx # Liste des conversations (filtrée par utilisateur)
│ │ └── SettingsPage.tsx # Token + gestion des utilisateurs virtuels
│ ├── App.tsx # Composant racine avec routing
│ ├── main.tsx # Point d'entrée React
│ └── index.css # Styles globaux (thème sombre)
├── docker-compose.yaml # Configuration Docker Compose
├── Dockerfile # Build multi-stage (Node)
└── tsconfig.server.json # Config TypeScript serveur
L'API utilise Server-Sent Events pour le streaming. Types d'événements :
| Événement | Description |
|---|---|
text-delta |
Fragment de texte de la réponse |
tool-call |
Début d'exécution d'un outil |
tool-result |
Résultat de l'exécution d'un outil |
artifacts |
Livres, musiques et suggestions |
cost |
Résumé des coûts |
done |
Fin du message avec statistiques |
error |
Erreur serveur |
Les appels d'outils sont représentés par des points colorés :
- Bleu (pulsant) : En cours d'exécution
- Vert : Succès
- Rouge : Erreur
Cliquez sur une carte de livre pour ouvrir un modal d'aperçu qui affiche :
- La couverture du livre (si disponible via
coverUrl) - Les informations de base (titre, auteur, année, ISBN)
- La présentation éditoriale récupérée via l'API
POST /api/halap/books/presentations - La description et les sujets du livre
L'API de présentations accepte jusqu'à 100 ISBN-13 par requête et retourne pour chacun :
found: trueavec le texte de présentationfound: falsesi le livre n'est pas trouvé
| Technologie | Version | Usage |
|---|---|---|
| React | 18.3 | Bibliothèque UI |
| TypeScript | 5.6 | Typage statique |
| Vite | 6.0 | Build et dev server |
| Hono | 4.x | Serveur backend / proxy |
| better-sqlite3 | - | Stockage tokens |
| Biome | 2.3 | Linting et formatage |
| react-markdown | 10.1 | Rendu Markdown |
| remark-gfm | 4.0 | Support GFM |
| halapi-js | submodule | SDK API client |
Le projet utilise une configuration stricte :
- TypeScript strict : Toutes les options strictes activées
- Biome : Linting et formatage automatiques
- Pas de
any: Types explicites partout - Type guards : Discrimination des unions pour les types Music
MIT