feat(auth): génération automatique du JWT secret au démarrage

- secret.ts: génère un secret aléatoire 96 chars hex si absent
- secret.ts: persiste dans data/secret.key (mode 600)
- secret.ts: priorité env JWT_SECRET > secret.key > génération
- auth: import JWT_SECRET depuis secret.ts au lieu de process.env
- docker-compose: JWT_SECRET optionnel (géré automatiquement)
- readme: mise à jour — JWT_SECRET n'est plus obligatoire
- feat: add favicon.ico
This commit is contained in:
MasutaYunikon committed 2026-03-11 15:45:00 +01:00
1 parent 867186db98
commit e7afe138e1
3 files changed
+48 -4

No files matched your search

+3 -3
View File
@@ -3,9 +3,9 @@ import path from 'path'
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
import type { Request, Response, NextFunction } from 'express'
import { JWT_SECRET } from './secret.js'
const DATA_PATH = path.join(process.cwd(), 'data', 'auth.json')
const JWT_SECRET = process.env.JWT_SECRET || 'fankarr-secret-change-me'
const SALT_ROUNDS = 10
interface AuthData {
@@ -63,7 +63,7 @@ export function authSetup(req: Request, res: Response): void {
const passwordHash = bcrypt.hashSync(password, SALT_ROUNDS)
writeAuth({ username, passwordHash })
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '30d' })
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '1d' })
res.cookie('fankarr_token', token, { httpOnly: true, sameSite: 'lax' })
res.json({ success: true })
}
@@ -87,7 +87,7 @@ export function authLogin(req: Request, res: Response): void {
return
}
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '30d' })
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: '1d' })
res.cookie('fankarr_token', token, { httpOnly: true, sameSite: 'lax' })
res.json({ success: true })
}
+44
View File
@@ -0,0 +1,44 @@
/**
* secret.ts
* =========
* Génère et persiste le JWT secret dans data/secret.key
* Si JWT_SECRET est défini en variable d'environnement, il est utilisé tel quel.
* Sinon, on lit data/secret.key — s'il n'existe pas, on en génère un et on le sauvegarde.
*/
import fs from 'fs'
import path from 'path'
import crypto from 'crypto'
const SECRET_PATH = path.join(process.cwd(), 'data', 'secret.key')
function generateSecret(): string {
return crypto.randomBytes(48).toString('hex')
}
function loadOrCreateSecret(): string {
// 1. Variable d'environnement — priorité absolue
if (process.env.JWT_SECRET && process.env.JWT_SECRET !== 'fankarr-secret-change-me') {
return process.env.JWT_SECRET
}
// 2. Fichier existant
if (fs.existsSync(SECRET_PATH)) {
const secret = fs.readFileSync(SECRET_PATH, 'utf-8').trim()
if (secret.length > 0) return secret
}
// 3. Génération + sauvegarde
const secret = generateSecret()
try {
fs.mkdirSync(path.dirname(SECRET_PATH), { recursive: true })
fs.writeFileSync(SECRET_PATH, secret, { encoding: 'utf-8', mode: 0o600 })
console.log('[auth] JWT secret généré et sauvegardé dans data/secret.key')
} catch (err) {
console.error('[auth] Impossible de sauvegarder le secret:', err)
}
return secret
}
export const JWT_SECRET: string = loadOrCreateSecret()
+1 -1
View File
@@ -273,7 +273,7 @@ async function download(key: string, torrent_url: string | null, magnet: string
downloading.value = key
const result = await store.download(torrent_url, magnet)
downloading.value = null
if (result.success) showToast('success', 'Ajouté à qBittorrent !')
if (result.success) showToast('success', 'Téléchargement lancé')
else showToast('error', result.error ?? 'Erreur inconnue')
}