187 lines
7.0 KiB
TypeScript
187 lines
7.0 KiB
TypeScript
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import crypto from 'crypto'
|
|
import { DATA_DIR } from './config.js'
|
|
import { logger } from './logger.js'
|
|
import { findById } from './users.js'
|
|
|
|
export type RequestStatus = 'pending' | 'approved' | 'rejected' | 'completed'
|
|
|
|
export interface Requester {
|
|
userId : string
|
|
username : string
|
|
seasons : number[] // numéros de saison ; [] = toutes les saisons disponibles
|
|
episodes : number[] // IDs d'épisodes ciblés ; [] = aucun
|
|
requestedAt: string
|
|
}
|
|
|
|
export interface SerieRequest {
|
|
id : string
|
|
serieId : number
|
|
serieName : string
|
|
requesters : Requester[]
|
|
status : RequestStatus
|
|
rejectionMessage?: string
|
|
createdAt : string
|
|
updatedAt : string
|
|
}
|
|
|
|
const REQUESTS_PATH = path.join(DATA_DIR, 'requests.json')
|
|
|
|
// ── Fichier des demandes ──────────────────────────────────────
|
|
|
|
export function readRequests(): SerieRequest[] {
|
|
try {
|
|
if (!fs.existsSync(REQUESTS_PATH)) return []
|
|
return JSON.parse(fs.readFileSync(REQUESTS_PATH, 'utf-8')) as SerieRequest[]
|
|
} catch { return [] }
|
|
}
|
|
|
|
function writeRequests(requests: SerieRequest[]): void {
|
|
fs.mkdirSync(path.dirname(REQUESTS_PATH), { recursive: true })
|
|
fs.writeFileSync(REQUESTS_PATH, JSON.stringify(requests, null, 2))
|
|
}
|
|
|
|
/** Saisons demandées par l'ensemble des demandeurs ([] = toutes). */
|
|
export function mergedSeasons(req: SerieRequest): number[] {
|
|
if (req.requesters.some(r => r.seasons.length === 0 && r.episodes.length === 0)) return []
|
|
const all = new Set<number>()
|
|
req.requesters.forEach(r => r.seasons.forEach(s => all.add(s)))
|
|
return [...all].sort((a, b) => a - b)
|
|
}
|
|
|
|
export function mergedEpisodes(req: SerieRequest): number[] {
|
|
const all = new Set<number>()
|
|
req.requesters.forEach(r => (r.episodes ?? []).forEach(e => all.add(e)))
|
|
return [...all].sort((a, b) => a - b)
|
|
}
|
|
|
|
// ── Création et suivi ─────────────────────────────────────────
|
|
|
|
export function upsertRequest(
|
|
userId : string,
|
|
serieId : number,
|
|
serieName: string,
|
|
seasons : number[], // [] = toutes les saisons
|
|
episodes : number[] = [],
|
|
): SerieRequest {
|
|
const user = findById(userId)
|
|
if (!user) throw new Error('Utilisateur introuvable')
|
|
|
|
const requests = readRequests()
|
|
|
|
const existing = requests.find(r =>
|
|
r.serieId === serieId &&
|
|
r.status !== 'rejected' &&
|
|
r.status !== 'completed'
|
|
)
|
|
|
|
const now = new Date().toISOString()
|
|
|
|
if (existing) {
|
|
const idx = existing.requesters.findIndex(r => r.userId === userId)
|
|
if (idx >= 0) {
|
|
const r = existing.requesters[idx]
|
|
const newIsAll = seasons.length === 0 && episodes.length === 0
|
|
const curIsAll = r.seasons.length === 0 && (r.episodes ?? []).length === 0
|
|
if (newIsAll || curIsAll) {
|
|
// Si l'une couvre toute la série : plus de filtre saison ni épisode
|
|
r.seasons = []
|
|
r.episodes = []
|
|
} else {
|
|
r.seasons = [...new Set([...r.seasons, ...seasons ])].sort((a, b) => a - b)
|
|
r.episodes = [...new Set([...(r.episodes ?? []), ...episodes ])].sort((a, b) => a - b)
|
|
}
|
|
r.requestedAt = now
|
|
} else {
|
|
existing.requesters.push({ userId, username: user.username, seasons, episodes, requestedAt: now })
|
|
}
|
|
existing.updatedAt = now
|
|
writeRequests(requests)
|
|
logger.info('requests', `Demande de « ${serieName} » complétée par « ${user.username} »`)
|
|
return existing
|
|
}
|
|
|
|
const request: SerieRequest = {
|
|
id : crypto.randomUUID(),
|
|
serieId,
|
|
serieName,
|
|
requesters: [{ userId, username: user.username, seasons, episodes, requestedAt: now }],
|
|
status : 'pending',
|
|
createdAt : now,
|
|
updatedAt : now,
|
|
}
|
|
writeRequests([...requests, request])
|
|
logger.info('requests', `Nouvelle demande de « ${serieName} » par « ${user.username} »`)
|
|
return request
|
|
}
|
|
|
|
export function approveRequest(id: string): SerieRequest {
|
|
const requests = readRequests()
|
|
const req = requests.find(r => r.id === id)
|
|
if (!req) throw new Error('Demande introuvable')
|
|
req.status = 'approved'
|
|
req.rejectionMessage = undefined
|
|
req.updatedAt = new Date().toISOString()
|
|
writeRequests(requests)
|
|
logger.info('requests', `Demande « ${req.serieName} » approuvée`)
|
|
return req
|
|
}
|
|
|
|
export function rejectRequest(id: string, message?: string): SerieRequest {
|
|
const requests = readRequests()
|
|
const req = requests.find(r => r.id === id)
|
|
if (!req) throw new Error('Demande introuvable')
|
|
req.status = 'rejected'
|
|
req.rejectionMessage = message
|
|
req.updatedAt = new Date().toISOString()
|
|
writeRequests(requests)
|
|
logger.info('requests', `Demande « ${req.serieName} » refusée`)
|
|
return req
|
|
}
|
|
|
|
export function completeRequest(id: string): SerieRequest {
|
|
const requests = readRequests()
|
|
const req = requests.find(r => r.id === id)
|
|
if (!req) throw new Error('Demande introuvable')
|
|
req.status = 'completed'
|
|
req.updatedAt = new Date().toISOString()
|
|
writeRequests(requests)
|
|
return req
|
|
}
|
|
|
|
export function deleteRequest(id: string): void {
|
|
const requests = readRequests()
|
|
if (!requests.find(r => r.id === id)) throw new Error('Demande introuvable')
|
|
writeRequests(requests.filter(r => r.id !== id))
|
|
}
|
|
|
|
/** Retire l'utilisateur d'une demande en attente ; supprime la demande s'il était seul. */
|
|
export function withdrawRequest(id: string, userId: string): { deleted: boolean; request: SerieRequest | null } {
|
|
const requests = readRequests()
|
|
const req = requests.find(r => r.id === id)
|
|
if (!req || !req.requesters.some(r => r.userId === userId)) throw new Error('Demande introuvable')
|
|
if (req.status !== 'pending') throw new Error('Seule une demande en attente peut être annulée')
|
|
|
|
const username = req.requesters.find(r => r.userId === userId)!.username
|
|
req.requesters = req.requesters.filter(r => r.userId !== userId)
|
|
if (req.requesters.length === 0) {
|
|
writeRequests(requests.filter(r => r.id !== id))
|
|
logger.info('requests', `Demande « ${req.serieName} » annulée par « ${username} »`)
|
|
return { deleted: true, request: null }
|
|
}
|
|
req.updatedAt = new Date().toISOString()
|
|
writeRequests(requests)
|
|
logger.info('requests', `« ${username} » retiré de la demande « ${req.serieName} »`)
|
|
return { deleted: false, request: req }
|
|
}
|
|
|
|
export function getRequestsForUser(userId: string): SerieRequest[] {
|
|
return readRequests().filter(r => r.requesters.some(req => req.userId === userId))
|
|
}
|
|
|
|
export function getPendingCount(): number {
|
|
return readRequests().filter(r => r.status === 'pending').length
|
|
}
|