101 lines
3.6 KiB
JavaScript
101 lines
3.6 KiB
JavaScript
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をインポート
|
||
|
||
// LibreOffice AppImageのパス
|
||
const libreofficeAppImage = './modules/LibreOffice-still.standard-x86_64.AppImage';
|
||
|
||
// tmpディレクトリのパス
|
||
const tmpDir = path.join(__dirname, 'tmp');
|
||
|
||
// tmpディレクトリが存在しない場合は作成
|
||
if (!fs.existsSync(tmpDir)) {
|
||
fs.mkdirSync(tmpDir);
|
||
}
|
||
|
||
// POSTエンドポイント
|
||
module.exports = (app) => {
|
||
app.post('/pdfConvert', 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キーが不正です' });
|
||
}
|
||
|
||
const { base64, fileName } = req.body;
|
||
if (!base64 || !fileName) {
|
||
return res.status(400).json({ error: 'base64とfileNameは必須です' });
|
||
}
|
||
const pdfBuffer = await convertBase64ToPdf(base64, 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 convertBase64ToPdf(base64Data, originalFileName) {
|
||
const tempId = uuidv4(); // 一意のIDを生成
|
||
const ext = path.extname(originalFileName) || '.tmp'; // 拡張子を取得、なければ.tmpを使用
|
||
const orgPath = path.join(tmpDir, `${tempId}${ext}`); // 一時的なファイルのパス
|
||
const pdfPath = path.join(tmpDir, `${tempId}.pdf`);
|
||
|
||
// base64データをファイルに保存
|
||
fs.writeFileSync(orgPath, Buffer.from(base64Data, 'base64'));
|
||
|
||
// LibreOfficeでPDF変換(タイムアウト60秒)
|
||
await new Promise((resolve, reject) => {
|
||
const child = execFile(
|
||
libreofficeAppImage,
|
||
[
|
||
'--headless',
|
||
'--convert-to', 'pdf',
|
||
'--outdir', tmpDir,
|
||
orgPath
|
||
],
|
||
(error, stdout, stderr) => {
|
||
if (error) {
|
||
console.error('stderr:', stderr);
|
||
reject(new Error(`LibreOfficeエラー: ${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(pdfPath)) {
|
||
throw new Error('PDF変換に失敗しました');
|
||
}
|
||
const pdfBuffer = fs.readFileSync(pdfPath);
|
||
|
||
// cleanup
|
||
try {
|
||
if (fs.existsSync(orgPath)) fs.unlinkSync(orgPath);
|
||
if (fs.existsSync(pdfPath)) fs.unlinkSync(pdfPath);
|
||
} catch (cleanupError) {
|
||
console.error('クリーンアップエラー:', cleanupError.message);
|
||
}
|
||
|
||
return pdfBuffer;
|
||
}
|
||
|