const fs = require('fs'); const path = require('path'); const { execFile } = require('child_process'); const { v4: uuidv4 } = require('uuid'); const express = require('express'); // JSON解析用にexpressをインポート const { API_KEY } = require('../config.js'); // API_KEYをインポート // tmpディレクトリのパス const tmpDir = path.join(__dirname, 'tmp'); // tmpディレクトリが存在しない場合は作成 if (!fs.existsSync(tmpDir)) { fs.mkdirSync(tmpDir); } // POSTエンドポイント module.exports = (app) => { app.post('/pdfMerge', express.json({ limit: '30mb' }), async (req, res) => { try { // APIキーのチェック const apiKey = req.headers['x-api-key']; if (!apiKey || apiKey !== API_KEY) { console.log('APIキーが不正です'); return res.status(401).json({ error: 'APIキーが不正です' }); } // base64_1, base64_2, fileName の3つをチェック const { base64_1, base64_2, fileName } = req.body; if (!base64_1 || !base64_2 || !fileName) { return res.status(400).json({ error: 'base64_1, base64_2, fileNameは必須です' }); } const pdfBuffer = await mergeBase64ToPdf(base64_1, base64_2, fileName); const safeFileName = encodeURIComponent(path.parse(fileName).name); // ファイル名をエスケープ res.set({ 'Content-Type': 'application/pdf', 'Content-Disposition': `attachment; filename="${safeFileName}.pdf"` }); res.send(pdfBuffer); } catch (err) { console.error('変換エラー:', err.message); res.status(500).json({ error: err.message }); } }); }; // PDF結合処理 async function mergeBase64ToPdf(base64Data1, base64Data2) { const tempId = uuidv4(); // 一意のIDを生成 const orgPath1 = path.join(tmpDir, `${tempId}_1.pdf`); const orgPath2 = path.join(tmpDir, `${tempId}_2.pdf`); const mergedPath = path.join(tmpDir, `${tempId}_merged.pdf`); // base64データをファイルに保存 fs.writeFileSync(orgPath1, Buffer.from(base64Data1, 'base64')); fs.writeFileSync(orgPath2, Buffer.from(base64Data2, 'base64')); // pdfuniteでPDF結合 await new Promise((resolve, reject) => { const child = execFile( 'pdfunite', [orgPath1, orgPath2, mergedPath], (error, stdout, stderr) => { if (error) { console.error('stderr:', stderr); reject(new Error(`pdfuniteエラー: ${stderr}`)); } else { resolve(); } } ); const timeout = setTimeout(() => { child.kill('SIGTERM'); // SIGTERMを使用してプロセスを終了 reject(new Error('PDF変換がタイムアウトしました(60秒経過)')); }, 60000); child.on('exit', () => clearTimeout(timeout)); }); // 結合されたPDFを読み込む if (!fs.existsSync(mergedPath)) { throw new Error('PDF結合に失敗しました'); } const pdfBuffer = fs.readFileSync(mergedPath); // cleanup try { if (fs.existsSync(orgPath1)) fs.unlinkSync(orgPath1); if (fs.existsSync(orgPath2)) fs.unlinkSync(orgPath2); if (fs.existsSync(mergedPath)) fs.unlinkSync(mergedPath); } catch (cleanupError) { console.error('クリーンアップエラー:', cleanupError.message); } return pdfBuffer; }