From 4b14d992afb3c37f4cff2884781492551925b147 Mon Sep 17 00:00:00 2001 From: Kenichiro NOGI Date: Sat, 19 Sep 2026 11:43:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20get-voice-mail=E3=81=AB=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E7=AF=84=E5=9B=B2=E3=83=95=E3=83=A9=E3=82=B0=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=80=81n8n=E3=83=AF=E3=83=BC=E3=82=AF=E3=83=95?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=82=921=E4=BB=B6=E3=81=9A=E3=81=A4?= =?UTF-8?q?=E3=83=AB=E3=83=BC=E3=83=97=E5=87=A6=E7=90=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 大量ファイル処理時のn8nメモリ枯渇対策として、SplitInBatches(1件ずつ)で SSHダウンロード→base64化→プリザンター登録を直列化(n8n側、API経由で反映済み)。 Webhookに?range=today|week|allを追加、app-portal側にも選択UIを実装。 --- NodeSrv/apps/app-portal/public/portal.js | 10 +++++++- NodeSrv/apps/app-portal/src/dashboard.js | 9 ++++++- NodeSrv/apps/app-portal/src/index.js | 4 +++- NodeSrv/apps/app-portal/src/portalMeta.js | 6 +++++ .../apps/app-portal/test/portalMeta.test.js | 24 +++++++++++++++++-- NodeSrv/apps/get-voice-mail/.env.example | 2 ++ NodeSrv/apps/get-voice-mail/src/index.js | 5 +++- 7 files changed, 54 insertions(+), 6 deletions(-) diff --git a/NodeSrv/apps/app-portal/public/portal.js b/NodeSrv/apps/app-portal/public/portal.js index 5d4fdba3..19401d21 100644 --- a/NodeSrv/apps/app-portal/public/portal.js +++ b/NodeSrv/apps/app-portal/public/portal.js @@ -34,7 +34,15 @@ document.getElementById('cards').addEventListener('click', async (event) => { return; } - const res = await fetch(`/api/compose/${composeId}/${action}`, { method: 'POST' }); + let url = `/api/compose/${composeId}/${action}`; + if (action === 'trigger') { + const rangeSelect = document.querySelector(`select[data-range-select][data-compose-id="${composeId}"]`); + if (rangeSelect) { + url += `?range=${encodeURIComponent(rangeSelect.value)}`; + } + } + + const res = await fetch(url, { method: 'POST' }); if (res.ok) { showToast(`${action} を実行しました`, false); } else { diff --git a/NodeSrv/apps/app-portal/src/dashboard.js b/NodeSrv/apps/app-portal/src/dashboard.js index b68d9a40..b4baa604 100644 --- a/NodeSrv/apps/app-portal/src/dashboard.js +++ b/NodeSrv/apps/app-portal/src/dashboard.js @@ -60,12 +60,19 @@ function renderCard(card) { meta.appType === 'web' && meta.url ? `開く` : ''; + const rangeSelect = + meta.appType === 'batch' && meta.rangeOptions && meta.rangeOptions.length > 0 + ? `` + : ''; const actionButtons = meta.appType === 'web' ? ` ` - : ``; + : `${rangeSelect} + `; return `
diff --git a/NodeSrv/apps/app-portal/src/index.js b/NodeSrv/apps/app-portal/src/index.js index 222571af..8be379f9 100644 --- a/NodeSrv/apps/app-portal/src/index.js +++ b/NodeSrv/apps/app-portal/src/index.js @@ -152,7 +152,9 @@ app.post('/api/compose/:composeId/trigger', async (req, res) => { res.sendStatus(400); return; } - const triggerRes = await fetch(`${meta.url}${meta.triggerPath}`, { + const range = req.query.range; + const query = meta.rangeOptions && meta.rangeOptions.includes(range) ? `?range=${encodeURIComponent(range)}` : ''; + const triggerRes = await fetch(`${meta.url}${meta.triggerPath}${query}`, { method: 'POST', headers: { 'X-Portal-Secret': PORTAL_SECRET }, }); diff --git a/NodeSrv/apps/app-portal/src/portalMeta.js b/NodeSrv/apps/app-portal/src/portalMeta.js index d730c806..3eab3221 100644 --- a/NodeSrv/apps/app-portal/src/portalMeta.js +++ b/NodeSrv/apps/app-portal/src/portalMeta.js @@ -22,11 +22,17 @@ function extractPortalMeta(envString, fallbackLabel) { if (appType !== 'web' && appType !== 'batch') { return null; } + const rangeOptions = vars.PORTAL_TRIGGER_RANGE_OPTIONS + ? vars.PORTAL_TRIGGER_RANGE_OPTIONS.split(',') + .map((s) => s.trim()) + .filter(Boolean) + : null; return { appType, label: vars.PORTAL_APP_LABEL || fallbackLabel, url: vars.PORTAL_APP_URL || null, triggerPath: vars.PORTAL_TRIGGER_PATH || null, + rangeOptions, }; } diff --git a/NodeSrv/apps/app-portal/test/portalMeta.test.js b/NodeSrv/apps/app-portal/test/portalMeta.test.js index de6dfe48..97e1a5ef 100644 --- a/NodeSrv/apps/app-portal/test/portalMeta.test.js +++ b/NodeSrv/apps/app-portal/test/portalMeta.test.js @@ -20,7 +20,13 @@ test('extractPortalMeta returns null when PORTAL_APP_TYPE is missing or invalid' test('extractPortalMeta parses a web-type app, falling back to label when PORTAL_APP_LABEL is absent', () => { const meta = extractPortalMeta('PORTAL_APP_TYPE=web\nPORTAL_APP_URL=https://x.apps.next-hd.net', 'x'); - assert.deepStrictEqual(meta, { appType: 'web', label: 'x', url: 'https://x.apps.next-hd.net', triggerPath: null }); + assert.deepStrictEqual(meta, { + appType: 'web', + label: 'x', + url: 'https://x.apps.next-hd.net', + triggerPath: null, + rangeOptions: null, + }); }); test('extractPortalMeta parses a batch-type app with PORTAL_TRIGGER_PATH', () => { @@ -28,5 +34,19 @@ test('extractPortalMeta parses a batch-type app with PORTAL_TRIGGER_PATH', () => 'PORTAL_APP_TYPE=batch\nPORTAL_APP_LABEL=同期バッチ\nPORTAL_APP_URL=http://sample:3000\nPORTAL_TRIGGER_PATH=/trigger', 'fallback' ); - assert.deepStrictEqual(meta, { appType: 'batch', label: '同期バッチ', url: 'http://sample:3000', triggerPath: '/trigger' }); + assert.deepStrictEqual(meta, { + appType: 'batch', + label: '同期バッチ', + url: 'http://sample:3000', + triggerPath: '/trigger', + rangeOptions: null, + }); +}); + +test('extractPortalMeta parses PORTAL_TRIGGER_RANGE_OPTIONS into an array', () => { + const meta = extractPortalMeta( + 'PORTAL_APP_TYPE=batch\nPORTAL_APP_URL=http://sample:3000\nPORTAL_TRIGGER_PATH=/trigger\nPORTAL_TRIGGER_RANGE_OPTIONS=today, week ,all', + 'fallback' + ); + assert.deepStrictEqual(meta.rangeOptions, ['today', 'week', 'all']); }); diff --git a/NodeSrv/apps/get-voice-mail/.env.example b/NodeSrv/apps/get-voice-mail/.env.example index 30322f34..b9d30adb 100644 --- a/NodeSrv/apps/get-voice-mail/.env.example +++ b/NodeSrv/apps/get-voice-mail/.env.example @@ -7,6 +7,8 @@ PORTAL_APP_LABEL=ボイスメール取得 PORTAL_APP_URL=http://get-voice-mail:3000 PORTAL_TRIGGER_PATH=/trigger PORTAL_SECRET= +# app-portalダッシュボードに取得範囲の選択肢を表示する(未指定ならボタンのみ、選択なしはtriggerで自動的にtoday扱い) +PORTAL_TRIGGER_RANGE_OPTIONS=today,week,all # n8nワークフロー入口(Webhookノード)のURL。/trigger起動時にPOSTする先 N8N_WEBHOOK_URL=https://n8n32.next-hd.net/webhook/get-voice-mail diff --git a/NodeSrv/apps/get-voice-mail/src/index.js b/NodeSrv/apps/get-voice-mail/src/index.js index c17f4586..b3fcf1e8 100644 --- a/NodeSrv/apps/get-voice-mail/src/index.js +++ b/NodeSrv/apps/get-voice-mail/src/index.js @@ -15,6 +15,8 @@ app.get('/health', (req, res) => { res.status(200).json({ status: 'healthy' }); }); +const VALID_RANGES = ['today', 'week', 'all']; + app.post('/trigger', (req, res) => { if (!isTriggerAuthorized(req.headers['x-portal-secret'], PORTAL_SECRET)) { res.sendStatus(403); @@ -25,7 +27,8 @@ app.post('/trigger', (req, res) => { res.sendStatus(500); return; } - fetch(N8N_WEBHOOK_URL, { + const range = VALID_RANGES.includes(req.query.range) ? req.query.range : 'today'; + fetch(`${N8N_WEBHOOK_URL}?range=${range}`, { method: 'POST', headers: N8N_WEBHOOK_SECRET ? { 'X-Api-Key': N8N_WEBHOOK_SECRET } : {}, }).catch((err) => {