Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91f5918233 | ||
|
|
71e3423ad4 | ||
|
|
15b4a99c86 | ||
|
|
14f02d1bdd | ||
|
|
ac6ac46a14 | ||
|
|
66f5771ca7 | ||
|
|
40a12a870b | ||
|
|
e7afe138e1 | ||
|
|
8656e7fa36 | ||
|
|
d21ddd6dfc | ||
|
|
867186db98 | ||
|
|
dd320e92dc |
No files matched your search
@@ -1,5 +1,26 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.6.0](https://github.com/Masutayunikon/FanKarr/compare/v1.5.0...v1.6.0) (2026-03-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add favicon ([dc4a2d4](https://github.com/Masutayunikon/FanKarr/commit/dc4a2d48f5c0cddd8e431ce33a69fc9146143fb6))
|
||||||
|
|
||||||
|
## [1.5.0](https://github.com/Masutayunikon/FanKarr/compare/v1.4.1...v1.5.0) (2026-03-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** génération automatique du JWT secret au démarrage ([eaa52e9](https://github.com/Masutayunikon/FanKarr/commit/eaa52e90900ff0e198beae190d7ad0298a702482))
|
||||||
|
|
||||||
|
## [1.4.1](https://github.com/Masutayunikon/FanKarr/compare/v1.4.0...v1.4.1) (2026-03-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* logger third args ([5abac1e](https://github.com/Masutayunikon/FanKarr/commit/5abac1e11236ed1e8f7f0de7f0cff881d1b60f10))
|
||||||
|
|
||||||
## [1.4.0](https://github.com/Masutayunikon/FanKarr/compare/v1.3.0...v1.4.0) (2026-03-11)
|
## [1.4.0](https://github.com/Masutayunikon/FanKarr/compare/v1.3.0...v1.4.0) (2026-03-11)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ Fankarr sera accessible sur `http://localhost:3001`.
|
|||||||
| `PUID` | `1000` | UID utilisateur pour les permissions fichiers |
|
| `PUID` | `1000` | UID utilisateur pour les permissions fichiers |
|
||||||
| `PGID` | `1000` | GID utilisateur pour les permissions fichiers |
|
| `PGID` | `1000` | GID utilisateur pour les permissions fichiers |
|
||||||
| `TZ` | — | Timezone (ex: `Europe/Paris`) |
|
| `TZ` | — | Timezone (ex: `Europe/Paris`) |
|
||||||
|
| `JWT_SECRET` | auto-généré | Secret JWT — généré automatiquement dans `data/secret.key` si absent |
|
||||||
| `GITHUB_RAW_URL` | repo scraper | URL du `torrent_final.json` à utiliser |
|
| `GITHUB_RAW_URL` | repo scraper | URL du `torrent_final.json` à utiliser |
|
||||||
|
|
||||||
### Premier démarrage
|
### Premier démarrage
|
||||||
@@ -165,4 +166,5 @@ Fankarr est disponible dans l'appstore personnalisé. Pour l'installer :
|
|||||||
|
|
||||||
- [fankai.fr](https://fankai.fr) — Le projet Fankai
|
- [fankai.fr](https://fankai.fr) — Le projet Fankai
|
||||||
- [Plugin Jellyfin Fankai](https://github.com/Nackophilz/fankai_jellyfin) — Reconnaissance des métadonnées dans Jellyfin
|
- [Plugin Jellyfin Fankai](https://github.com/Nackophilz/fankai_jellyfin) — Reconnaissance des métadonnées dans Jellyfin
|
||||||
- [fankarr-scraper](https://github.com/masutayunikon/fankarr-scraper) — Pipeline de collecte des torrents
|
- [fankarr-scraper](https://github.com/masutayunikon/fankarr-scraper) — Pipeline de collecte des torrents
|
||||||
|
- [runtipi-appstore](https://github.com/Masutayunikon/runtipi-appstore) — Appstore Runtipi personnel
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
+3
-3
@@ -3,9 +3,9 @@ import path from 'path'
|
|||||||
import bcrypt from 'bcrypt'
|
import bcrypt from 'bcrypt'
|
||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import type { Request, Response, NextFunction } from 'express'
|
import type { Request, Response, NextFunction } from 'express'
|
||||||
|
import { JWT_SECRET } from './secret.js'
|
||||||
|
|
||||||
const DATA_PATH = path.join(process.cwd(), 'data', 'auth.json')
|
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
|
const SALT_ROUNDS = 10
|
||||||
|
|
||||||
interface AuthData {
|
interface AuthData {
|
||||||
@@ -63,7 +63,7 @@ export function authSetup(req: Request, res: Response): void {
|
|||||||
const passwordHash = bcrypt.hashSync(password, SALT_ROUNDS)
|
const passwordHash = bcrypt.hashSync(password, SALT_ROUNDS)
|
||||||
writeAuth({ username, passwordHash })
|
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.cookie('fankarr_token', token, { httpOnly: true, sameSite: 'lax' })
|
||||||
res.json({ success: true })
|
res.json({ success: true })
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ export function authLogin(req: Request, res: Response): void {
|
|||||||
return
|
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.cookie('fankarr_token', token, { httpOnly: true, sameSite: 'lax' })
|
||||||
res.json({ success: true })
|
res.json({ success: true })
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ export async function autoOrganizeAll(
|
|||||||
try {
|
try {
|
||||||
torrents = await listFn()
|
torrents = await listFn()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('organize', "Impossible de récupérer la liste des torrents:", err)
|
logger.error('organize', "Impossible de récupérer la liste des torrents: " + (err instanceof Error ? err.message : String(err)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -273,7 +273,7 @@ async function download(key: string, torrent_url: string | null, magnet: string
|
|||||||
downloading.value = key
|
downloading.value = key
|
||||||
const result = await store.download(torrent_url, magnet)
|
const result = await store.download(torrent_url, magnet)
|
||||||
downloading.value = null
|
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')
|
else showToast('error', result.error ?? 'Erreur inconnue')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user