feat: add persistent PDF storage with session management across restarts

This commit is contained in:
ɧσℓσ
2025-12-10 19:21:34 +01:00
parent e223be9452
commit 67379be432
3 changed files with 52 additions and 4 deletions

View File

@@ -16,7 +16,7 @@ RUN apk add --no-cache python3 make g++ cairo-dev pango-dev libjpeg-turbo-dev gi
COPY src/ ./src/ COPY src/ ./src/
# Crée les répertoires pour les volumes # Crée les répertoires pour les volumes
RUN mkdir -p /app/config RUN mkdir -p /app/config /app/data/pdfs
# Crée le fichier projects.json # Crée le fichier projects.json
COPY config/projects.json.example /app/config/projects.json COPY config/projects.json.example /app/config/projects.json

View File

@@ -22,6 +22,7 @@ services:
# Volumes pour la configuration # Volumes pour la configuration
volumes: volumes:
- ./config/projects.json:/app/config/projects.json:ro # Fichier projects.json en lecture seule - ./config/projects.json:/app/config/projects.json:ro # Fichier projects.json en lecture seule
- pdf-data:/app/data/pdfs #
# Réseau # Réseau
networks: networks:

View File

@@ -13,8 +13,12 @@ const logger = require('../../utils/logger');
*/ */
class PdfHandler { class PdfHandler {
constructor() { constructor() {
this.storageDir = process.env.PDF_STORAGE_DIR || path.join(process.cwd(), 'data', 'pdfs');
this.sessionFile = path.join(this.storageDir, 'sessions.json');
this.sessions = new Map(); // messageId -> { pdfPath, totalPages, ownerId, createdAt, downloadUrl, isPublic } this.sessions = new Map(); // messageId -> { pdfPath, totalPages, ownerId, createdAt, downloadUrl, isPublic }
pdfjsLib.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.min.js'); pdfjsLib.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.min.js');
this.ensureStorageDir();
this.loadSessions();
} }
/** /**
@@ -76,6 +80,7 @@ class PdfHandler {
downloadUrl: sourceUrl, downloadUrl: sourceUrl,
isPublic isPublic
}); });
this.saveSessions();
logger.info(`PDF affiché (${totalPages} pages) par ${interaction.user.tag}`); logger.info(`PDF affiché (${totalPages} pages) par ${interaction.user.tag}`);
@@ -135,9 +140,10 @@ class PdfHandler {
*/ */
async downloadPdf(url) { async downloadPdf(url) {
const response = await axios.get(url, { responseType: 'arraybuffer' }); const response = await axios.get(url, { responseType: 'arraybuffer' });
const tmpPath = path.join(os.tmpdir(), `pdf-${Date.now()}-${Math.random().toString(36).slice(2)}.pdf`); const fileName = `pdf-${Date.now()}-${Math.random().toString(36).slice(2)}.pdf`;
fs.writeFileSync(tmpPath, response.data); const targetPath = path.join(this.storageDir, fileName);
return tmpPath; fs.writeFileSync(targetPath, response.data);
return targetPath;
} }
/** /**
@@ -222,6 +228,47 @@ class PdfHandler {
return 'Visualisation PDF'; return 'Visualisation PDF';
} }
} }
/**
* Persistance des sessions pour survivre aux redémarrages
*/
ensureStorageDir() {
fs.mkdirSync(this.storageDir, { recursive: true });
}
loadSessions() {
try {
if (!fs.existsSync(this.sessionFile)) return;
const raw = fs.readFileSync(this.sessionFile, 'utf8');
const data = JSON.parse(raw);
data.forEach(entry => {
if (entry.pdfPath && fs.existsSync(entry.pdfPath)) {
this.sessions.set(entry.messageId, {
pdfPath: entry.pdfPath,
totalPages: entry.totalPages,
ownerId: entry.ownerId,
createdAt: entry.createdAt,
downloadUrl: entry.downloadUrl,
isPublic: entry.isPublic
});
}
});
} catch (e) {
logger.error('Erreur lors du chargement des sessions PDF persistées:', e);
}
}
saveSessions() {
try {
const data = [];
for (const [messageId, session] of this.sessions.entries()) {
data.push({ messageId, ...session });
}
fs.writeFileSync(this.sessionFile, JSON.stringify(data, null, 2), 'utf8');
} catch (e) {
logger.error('Erreur lors de la sauvegarde des sessions PDF:', e);
}
}
} }
module.exports = PdfHandler; module.exports = PdfHandler;