Fix: favicon 404 error and NIP-95 upload 500 error
**Motivations:** - Corriger l'erreur 404 pour favicon.ico demandé par les navigateurs - Corriger l'erreur 500 de l'API NIP-95 upload empêchant les uploads de fichiers **Root causes:** - Fichier favicon.ico manquant dans public/ causant des erreurs 404 répétées - Incompatibilité entre form-data (npm) et fetch() natif de Node.js dans l'API NIP-95 **Correctifs:** - Ajout de favicon.svg et mise à jour des références dans les pages - Remplacement de fetch() par https/http natifs de Node.js dans nip95-upload.ts - Amélioration de la gestion des erreurs et nettoyage des fichiers temporaires **Evolutions:** - Documentation des problèmes et solutions dans fixKnowledge/ **Pages affectées:** - components/HomeView.tsx - pages/docs.tsx - pages/presentation.tsx - pages/api/nip95-upload.ts - features/account-creation-buttons-separation.md - fixKnowledge/favicon-404-error.md (nouveau) - fixKnowledge/nip95-upload-500-error.md (nouveau) - public/favicon.svg (nouveau)
This commit is contained in:
parent
83e9029f9a
commit
065ab30828
@ -33,7 +33,7 @@ function HomeHead() {
|
||||
content="Plateforme de publication d'articles scientifiques et de science-fiction avec sponsoring et rémunération des avis"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,3 +51,5 @@ Aucun déploiement spécial nécessaire. Les modifications sont purement fronten
|
||||
- Le composant `NoAccountActionButtons` respecte la limite de lignes de fonction
|
||||
- La fonction `NoAccountView` respecte la limite de 40 lignes après refactorisation
|
||||
|
||||
|
||||
|
||||
|
||||
57
fixKnowledge/favicon-404-error.md
Normal file
57
fixKnowledge/favicon-404-error.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Problème : favicon.ico retourne 404
|
||||
|
||||
## Date
|
||||
2025-01-27
|
||||
|
||||
## Problème
|
||||
Le navigateur demandait automatiquement `/favicon.ico` mais le fichier n'existait pas dans le dossier `public/`, causant une erreur 404.
|
||||
|
||||
## Symptômes
|
||||
- Console navigateur : `Failed to load resource: the server responded with a status of 404 ()`
|
||||
- Erreur répétée sur toutes les pages : `favicon.ico:1 Failed to load resource: the server responded with a status of 404 ()`
|
||||
- Aucun favicon affiché dans les onglets du navigateur
|
||||
|
||||
## Root cause
|
||||
Le fichier `favicon.ico` n'existait pas dans le dossier `public/`, mais plusieurs pages référençaient `/favicon.ico` dans leur balise `<Head>` :
|
||||
- `components/HomeView.tsx`
|
||||
- `pages/presentation.tsx`
|
||||
- `pages/docs.tsx`
|
||||
|
||||
Les navigateurs demandent automatiquement `/favicon.ico` même si une autre icône est spécifiée, ce qui causait l'erreur 404.
|
||||
|
||||
## Impact
|
||||
- Erreurs 404 répétées dans les logs du serveur
|
||||
- Aucun favicon affiché dans les onglets du navigateur
|
||||
- Expérience utilisateur dégradée
|
||||
|
||||
## Correctifs
|
||||
1. Création d'un fichier `favicon.svg` minimal dans `public/`
|
||||
2. Mise à jour des références dans les fichiers pour pointer vers `/favicon.svg` au lieu de `/favicon.ico`
|
||||
3. Ajout du type MIME `image/svg+xml` dans les balises `<link>`
|
||||
|
||||
## Modifications
|
||||
- **Fichier créé** : `public/favicon.svg` (SVG minimal avec un rectangle cyan)
|
||||
- **Fichiers modifiés** :
|
||||
- `components/HomeView.tsx` : `<link rel="icon" href="/favicon.svg" type="image/svg+xml" />`
|
||||
- `pages/presentation.tsx` : `<link rel="icon" href="/favicon.svg" type="image/svg+xml" />`
|
||||
- `pages/docs.tsx` : `<link rel="icon" href="/favicon.svg" type="image/svg+xml" />`
|
||||
|
||||
## Modalités de déploiement
|
||||
1. Les modifications sont dans le code source
|
||||
2. Le fichier `favicon.svg` est servi automatiquement par Next.js depuis le dossier `public/`
|
||||
3. Rebuild de l'application : `npm run build`
|
||||
4. Redémarrage du service Next.js si nécessaire
|
||||
5. Aucune configuration supplémentaire nécessaire
|
||||
|
||||
## Modalités d'analyse
|
||||
Pour vérifier si le problème existe :
|
||||
1. Vérifier la présence du fichier `public/favicon.svg`
|
||||
2. Vérifier les références dans les fichiers HTML (balises `<link rel="icon">`)
|
||||
3. Tester dans le navigateur : accéder à `/favicon.svg` et vérifier qu'il est servi
|
||||
4. Vérifier les logs du serveur pour les erreurs 404 sur `/favicon.ico`
|
||||
|
||||
## Notes
|
||||
- Les navigateurs modernes supportent les SVG comme favicon
|
||||
- SVG est plus léger et plus flexible qu'un fichier .ico
|
||||
- Si nécessaire, on peut créer un fichier `.ico` en plus pour la compatibilité avec les anciens navigateurs
|
||||
- Le favicon SVG actuel est minimal (rectangle cyan) et peut être remplacé par un design plus élaboré si nécessaire
|
||||
63
fixKnowledge/nip95-upload-500-error.md
Normal file
63
fixKnowledge/nip95-upload-500-error.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Problème : API NIP-95 upload retourne 500
|
||||
|
||||
## Date
|
||||
2025-01-27
|
||||
|
||||
## Problème
|
||||
L'endpoint API `/api/nip95-upload` retournait une erreur 500 lors des tentatives d'upload de fichiers via NIP-95.
|
||||
|
||||
## Symptômes
|
||||
- Erreur 500 lors des appels à `/api/nip95-upload?endpoint=https://void.cat/upload`
|
||||
- Console navigateur : `Failed to load resource: the server responded with a status of 500 ()`
|
||||
- Console navigateur : `NIP-95 upload endpoint error: Object`
|
||||
- Les uploads NIP-95 échouaient systématiquement
|
||||
|
||||
## Root cause
|
||||
Le package npm `form-data` n'est pas compatible avec `fetch()` natif de Node.js (Node 18+). L'API utilisait `form-data` de npm avec `fetch()` natif, ce qui causait une incompatibilité car :
|
||||
- `form-data` (npm) utilise des streams Node.js natifs et l'API `getHeaders()` pour obtenir les headers avec le boundary
|
||||
- `fetch()` natif de Node.js attend le FormData du web standard (disponible dans le navigateur), pas le package npm `form-data`
|
||||
- Cette incompatibilité provoquait une erreur lors de l'exécution de la requête HTTP
|
||||
|
||||
## Impact
|
||||
- Impossible d'uploader des fichiers via NIP-95
|
||||
- Les utilisateurs ne pouvaient pas publier d'articles avec des médias
|
||||
- Fonctionnalité d'upload complètement non fonctionnelle
|
||||
|
||||
## Correctifs
|
||||
1. Remplacement de `fetch()` natif par les modules natifs `https` et `http` de Node.js
|
||||
2. Utilisation de `form-data.pipe()` pour envoyer les données via les modules natifs
|
||||
3. Gestion correcte des erreurs et nettoyage des fichiers temporaires dans tous les cas
|
||||
|
||||
## Modifications
|
||||
- **Fichier modifié** : `pages/api/nip95-upload.ts`
|
||||
- **Imports ajoutés** :
|
||||
```typescript
|
||||
import https from 'https'
|
||||
import http from 'http'
|
||||
import { URL } from 'url'
|
||||
```
|
||||
- **Imports supprimés** :
|
||||
```typescript
|
||||
import { Readable } from 'stream' // Non utilisé
|
||||
```
|
||||
- **Remplacement de fetch()** : Utilisation de `https.request()` ou `http.request()` selon le protocole de l'URL cible
|
||||
- **Gestion des erreurs améliorée** : Nettoyage des fichiers temporaires même en cas d'erreur de requête
|
||||
- **Gestion des streams** : Utilisation de `formData.pipe(proxyRequest)` pour envoyer les données
|
||||
|
||||
## Modalités de déploiement
|
||||
1. Les modifications sont dans le code source
|
||||
2. Rebuild de l'application : `npm run build`
|
||||
3. Redémarrage du service Next.js
|
||||
4. Aucune dépendance supplémentaire nécessaire (utilisation des modules natifs Node.js)
|
||||
|
||||
## Modalités d'analyse
|
||||
Pour vérifier si le problème existe :
|
||||
1. Vérifier les logs du serveur pour les erreurs liées à `form-data` ou `fetch`
|
||||
2. Tester l'upload via l'interface utilisateur
|
||||
3. Vérifier les logs de la console navigateur pour les erreurs 500
|
||||
4. Vérifier que `form-data` (npm) est utilisé avec `https`/`http` natif, pas avec `fetch()`
|
||||
|
||||
## Notes
|
||||
- Le package `form-data` (npm) doit être utilisé avec les modules `https`/`http` natifs de Node.js, pas avec `fetch()`
|
||||
- `fetch()` natif de Node.js est compatible avec FormData du web standard (disponible dans le navigateur), pas avec le package npm `form-data`
|
||||
- Les fichiers temporaires créés par formidable doivent être nettoyés même en cas d'erreur pour éviter l'accumulation de fichiers
|
||||
@ -2,7 +2,9 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { IncomingForm, File as FormidableFile } from 'formidable'
|
||||
import FormData from 'form-data'
|
||||
import fs from 'fs'
|
||||
import { Readable } from 'stream'
|
||||
import https from 'https'
|
||||
import http from 'http'
|
||||
import { URL } from 'url'
|
||||
|
||||
const MAX_FILE_SIZE = 50 * 1024 * 1024 // 50MB
|
||||
|
||||
@ -45,7 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
})
|
||||
})
|
||||
|
||||
const { fields, files } = parseResult
|
||||
const { files } = parseResult
|
||||
|
||||
// Get the file from the parsed form
|
||||
const fileField = files.file?.[0]
|
||||
@ -61,25 +63,60 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
contentType: fileField.mimetype || 'application/octet-stream',
|
||||
})
|
||||
|
||||
// Forward to target endpoint
|
||||
let response: Response
|
||||
// Forward to target endpoint using https/http native modules
|
||||
const targetUrl = new URL(targetEndpoint)
|
||||
const isHttps = targetUrl.protocol === 'https:'
|
||||
const clientModule = isHttps ? https : http
|
||||
|
||||
let response: { statusCode: number; statusMessage: string; body: string }
|
||||
try {
|
||||
response = await fetch(targetEndpoint, {
|
||||
response = await new Promise<{ statusCode: number; statusMessage: string; body: string }>((resolve, reject) => {
|
||||
const headers = formData.getHeaders()
|
||||
const requestOptions = {
|
||||
hostname: targetUrl.hostname,
|
||||
port: targetUrl.port || (isHttps ? 443 : 80),
|
||||
path: targetUrl.pathname + targetUrl.search,
|
||||
method: 'POST',
|
||||
body: formData as unknown as BodyInit,
|
||||
headers: {
|
||||
...formData.getHeaders(),
|
||||
},
|
||||
headers: headers,
|
||||
}
|
||||
|
||||
const proxyRequest = clientModule.request(requestOptions, (proxyResponse) => {
|
||||
let body = ''
|
||||
proxyResponse.setEncoding('utf8')
|
||||
proxyResponse.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
} catch (fetchError) {
|
||||
proxyResponse.on('end', () => {
|
||||
resolve({
|
||||
statusCode: proxyResponse.statusCode || 500,
|
||||
statusMessage: proxyResponse.statusMessage || 'Internal Server Error',
|
||||
body: body,
|
||||
})
|
||||
})
|
||||
proxyResponse.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
||||
proxyRequest.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
|
||||
formData.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
|
||||
formData.pipe(proxyRequest)
|
||||
})
|
||||
} catch (requestError) {
|
||||
// Clean up temporary file before returning error
|
||||
try {
|
||||
fs.unlinkSync(fileField.filepath)
|
||||
} catch (unlinkError) {
|
||||
console.error('Error deleting temp file:', unlinkError)
|
||||
}
|
||||
const errorMessage = fetchError instanceof Error ? fetchError.message : 'Unknown fetch error'
|
||||
console.error('NIP-95 proxy fetch error:', {
|
||||
const errorMessage = requestError instanceof Error ? requestError.message : 'Unknown request error'
|
||||
console.error('NIP-95 proxy request error:', {
|
||||
targetEndpoint,
|
||||
error: errorMessage,
|
||||
fileSize: fileField.size,
|
||||
@ -97,20 +134,34 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
console.error('Error deleting temp file:', unlinkError)
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
const errorText = response.body.substring(0, 200) // Limit log size
|
||||
console.error('NIP-95 proxy response error:', {
|
||||
targetEndpoint,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
errorText: errorText.substring(0, 200), // Limit log size
|
||||
status: response.statusCode,
|
||||
statusText: response.statusMessage,
|
||||
errorText: errorText,
|
||||
})
|
||||
return res.status(response.status).json({
|
||||
error: errorText || `Upload failed: ${response.status} ${response.statusText}`,
|
||||
return res.status(response.statusCode).json({
|
||||
error: errorText || `Upload failed: ${response.statusCode} ${response.statusMessage}`,
|
||||
})
|
||||
}
|
||||
|
||||
let result: unknown
|
||||
try {
|
||||
result = JSON.parse(response.body)
|
||||
} catch (parseError) {
|
||||
const errorMessage = parseError instanceof Error ? parseError.message : 'Invalid JSON response'
|
||||
console.error('NIP-95 proxy JSON parse error:', {
|
||||
targetEndpoint,
|
||||
error: errorMessage,
|
||||
bodyPreview: response.body.substring(0, 100),
|
||||
})
|
||||
return res.status(500).json({
|
||||
error: `Invalid upload response: ${errorMessage}`,
|
||||
})
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
return res.status(200).json(result)
|
||||
} catch (error) {
|
||||
console.error('NIP-95 proxy error:', error)
|
||||
|
||||
@ -46,7 +46,7 @@ export default function DocsPage() {
|
||||
<title>{t('nav.documentation')} - zapwall.fr</title>
|
||||
<meta name="description" content="Documentation complète pour zapwall.fr" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
</Head>
|
||||
<main className="min-h-screen bg-cyber-darker">
|
||||
<PageHeader />
|
||||
|
||||
@ -37,7 +37,7 @@ function PresentationLayout() {
|
||||
content={t('presentation.description')}
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
</Head>
|
||||
|
||||
<main className="min-h-screen bg-cyber-darker">
|
||||
|
||||
4
public/favicon.svg
Normal file
4
public/favicon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<rect width="16" height="16" fill="#00ffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 178 B |
Loading…
x
Reference in New Issue
Block a user