V1 Fonctionnelle (pas de secu de l'api, ni front quali)

This commit is contained in:
ElPoyo
2025-09-27 11:08:24 +02:00
parent 65846640cf
commit b4a468c14b
14 changed files with 2132 additions and 353 deletions

View File

@@ -1,148 +1,195 @@
/**
* Service pour communiquer avec l'API backend des textes en patois
* Service pour communiquer avec l'API backend des textes
*/
const API_BASE_URL = 'http://localhost:3001/api'
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001/api'
export class TextService {
constructor() {
this.cache = new Map()
this.CACHE_DURATION = 5 * 60 * 1000 // 5 minutes
}
/**
* Effectue une requête HTTP vers l'API
*/
async fetchAPI(endpoint, options = {}) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.error || `Erreur HTTP: ${response.status}`)
}
return await response.json()
} catch (error) {
console.error(`Erreur API ${endpoint}:`, error)
throw error
}
}
/**
* Charge un texte spécifique par son ID
*/
async loadText(textId) {
const cacheKey = `text-${textId}`
// Vérifier le cache
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)
if (Date.now() - cached.timestamp < this.CACHE_DURATION) {
return cached.data
}
constructor() {
this.cache = new Map()
this.CACHE_DURATION = 5 * 60 * 1000 // 5 minutes
}
try {
const textData = await this.fetchAPI(`/texts/${textId}`)
/**
* Effectue une requête HTTP vers l'API
*/
async fetchAPI(endpoint, options = {}) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
})
// Mettre en cache
this.cache.set(cacheKey, {
data: textData,
timestamp: Date.now()
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.error || `Erreur HTTP: ${response.status}`)
}
return textData
} catch (error) {
throw new Error(`Impossible de charger le texte "${textId}": ${error.message}`)
return await response.json()
} catch (error) {
console.error(`Erreur API ${endpoint}:`, error)
throw error
}
}
}
ni
/**
* Charge un texte spécifique par son ID
*/
async loadText(textId) {
const cacheKey = `text-${textId}`
/**
* Recherche dans les textes
const cacheKey = 'all-texts'
// Vérifier le cache
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)
if (Date.now() - cached.timestamp < this.CACHE_DURATION) {
return cached.data
}
}
// Vérifier le cache
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)
if (Date.now() - cached.timestamp < this.CACHE_DURATION) {
return cached.data
}
try {
const textData = await this.fetchAPI(`/texts/${textId}`)
// Mettre en cache
this.cache.set(cacheKey, {
data: textData,
timestamp: Date.now()
})
return textData
} catch (error) {
throw new Error(`Impossible de charger le texte "${textId}": ${error.message}`)
}
}
/**
* Charge la liste de tous les textes disponibles
*/
async loadAllTexts() {
const cacheKey = 'all-texts'
// Vérifier le cache
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)
if (Date.now() - cached.timestamp < this.CACHE_DURATION) {
return cached.data
}
}
try {
const texts = await this.fetchAPI('/texts')
// Mettre en cache
this.cache.set(cacheKey, {
data: texts,
timestamp: Date.now()
})
return texts
} catch (error) {
throw new Error(`Impossible de charger la liste des textes: ${error.message}`)
}
}
try {
const texts = await this.fetchAPI('/texts')
// Mettre en cache
this.cache.set(cacheKey, {
data: texts,
timestamp: Date.now()
})
text.metadata.titre_pt?.toLowerCase().includes(searchTerm) ||
text.frenchText?.toLowerCase().includes(searchTerm) ||
)
throw new Error(`Impossible de charger la liste des textes: ${error.message}`)
/**
* Recherche dans les textes avec filtres
*/
async searchTexts(query, filters = {}) {
try {
const params = new URLSearchParams()
// Filtres
if (filters.category) {
results = results.filter(text => text.metadata.categorie === filters.category)
* Recherche dans les textes avec filtres
if (query && query.trim()) {
params.append('search', query.trim())
}
if (filters.difficulty) {
try {
const params = new URLSearchParams()
if (filters.category) {
params.append('category', filters.category)
}
if (query && query.trim()) {
params.append('search', query.trim())
}
if (filters.difficulty) {
params.append('difficulty', filters.difficulty)
}
if (filters.category) {
params.append('category', filters.category)
}
if (filters.onlyWithAudio) {
params.append('onlyWithAudio', 'true')
}
if (filters.difficulty) {
params.append('difficulty', filters.difficulty)
}
if (filters.onlyWithAudio) {
params.append('onlyWithAudio', 'true')
}
const endpoint = params.toString() ? `/texts?${params.toString()}` : '/texts'
return await this.fetchAPI(endpoint)
} catch (error) {
throw new Error(`Erreur lors de la recherche: ${error.message}`)
}
}
const endpoint = params.toString() ? `/texts?${params.toString()}` : '/texts'
return await this.fetchAPI(endpoint)
} catch (error) {
throw new Error(`Erreur lors de la recherche: ${error.message}`)
}
const authors = new Set()
const categories = new Set()
let withAudio = 0
for (const text of this.textsList) {
if (text.metadata.auteur) authors.add(text.metadata.auteur)
try {
return await this.fetchAPI('/random')
} catch (error) {
throw new Error(`Impossible d'obtenir un texte aléatoire: ${error.message}`)
/**
* Obtient un texte aléatoire
*/
async getRandomText() {
try {
return await this.fetchAPI('/random')
} catch (error) {
throw new Error(`Impossible d'obtenir un texte aléatoire: ${error.message}`)
}
}
/**
* Obtient les statistiques de la collection
*/
async getStats() {
const cacheKey = 'stats'
// Vérifier le cache
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)
if (Date.now() - cached.timestamp < this.CACHE_DURATION) {
return cached.data
}
}
try {
const stats = await this.fetchAPI('/stats')
// Mettre en cache
this.cache.set(cacheKey, {
data: stats,
timestamp: Date.now()
})
return stats
} catch (error) {
throw new Error(`Impossible de charger les statistiques: ${error.message}`)
}
}
/**
* Obtient l'URL du fichier audio pour un texte
*/
getAudioUrl(textId) {
return `${API_BASE_URL}/texts/${textId}/audio`
}
/**
* Vérifie si l'API backend est accessible
*/
async checkHealth() {
try {
const health = await this.fetchAPI('/health')
return health.status === 'OK'
} catch (error) {
console.warn('API backend non accessible:', error.message)
return false
}
}
/**
* Vide le cache (utile pour forcer le rechargement)
*/
clearCache() {
this.cache.clear()
}
}
}
// Instance singleton
export const textService = new TextService()
const cacheKey = 'stats'
// Vérifier le cache
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey)
if (Date.now() - cached.timestamp < this.CACHE_DURATION) {
return cached.data
}