Add n8n service to docker-compose
- docker-compose: n8n container (port 5678) with PostgreSQL backend - n8n/: 7 TypeScript workflow files + import script + README - Workflows: 4 CMS BI data ingestion + 3 email reports - README: updated with n8n env vars and port - .dockerignore: exclude import.sh from container
This commit is contained in:
37
n8n/README.md
Normal file
37
n8n/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# n8n Workflows
|
||||
|
||||
Included in the Docker deployment. Workflows mount at `/workflows/`.
|
||||
|
||||
## After first boot
|
||||
|
||||
1. Open http://localhost:5678 and create your owner account
|
||||
2. Go to **Settings → n8n API** → Generate an API key
|
||||
3. Create these credentials in n8n:
|
||||
|
||||
| Credential Name | Type | Config |
|
||||
|-----------------|------|--------|
|
||||
| `jetour-bi-remote` | PostgreSQL | Host: `db`, Port: `5432`, DB: `jetour-bi`, User: `jetour`, Password: (your DB_PASSWORD) |
|
||||
| `jetour-smtp` | SMTP | Your email server details |
|
||||
|
||||
4. Import workflows:
|
||||
```bash
|
||||
docker compose exec n8n sh /workflows/import.sh
|
||||
```
|
||||
|
||||
## Workflows
|
||||
|
||||
| File | Trigger | What it does |
|
||||
|------|---------|-------------|
|
||||
| `wf1_prospects_created.ts` | 02:00 SAST | Fetch new prospects from CMS BI API |
|
||||
| `wf2_prospects_updated.ts` | 02:10 SAST | Fetch prospect changes from CMS BI |
|
||||
| `wf3_otps_created.ts` | 02:20 SAST | Fetch new OTPs from CMS BI |
|
||||
| `wf4_otps_updated.ts` | 02:50 SAST | Fetch OTP changes from CMS BI |
|
||||
| `wf_email_daily.ts` | 07:00 SAST | POST /api/send-report/daily → email |
|
||||
| `wf_email_weekly.ts` | Mon 07:00 | POST /api/send-report/weekly → email |
|
||||
| `wf_email_monthly.ts` | 1st 07:00 | POST /api/send-report/monthly → email |
|
||||
|
||||
## Manual import (alternative)
|
||||
|
||||
If the script fails, use n8n's built-in import in the UI:
|
||||
1. Go to **Workflows → Import from File**
|
||||
2. Select each `.ts` file in this directory
|
||||
34
n8n/import.sh
Executable file
34
n8n/import.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# n8n Workflow Import — run after n8n starts
|
||||
# Usage: docker compose exec n8n /workflows/import.sh
|
||||
set -e
|
||||
|
||||
N8N_URL="http://localhost:5678"
|
||||
WORKFLOWS_DIR="/workflows"
|
||||
|
||||
echo "Waiting for n8n..."
|
||||
for i in $(seq 1 20); do
|
||||
if curl -s -o /dev/null "$N8N_URL/healthz" 2>/dev/null; then
|
||||
echo "n8n ready"
|
||||
break
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "To import workflows, you need an n8n API key."
|
||||
echo "1. Open http://localhost:5678 and create your owner account"
|
||||
echo "2. Go to Settings → n8n API → Generate API Key"
|
||||
echo "3. Run: N8N_API_KEY=your-key $0"
|
||||
echo ""
|
||||
echo "Workflow files available in $WORKFLOWS_DIR:"
|
||||
ls -1 "$WORKFLOWS_DIR"/*.ts 2>/dev/null | while read f; do
|
||||
echo " $(basename $f)"
|
||||
done
|
||||
echo ""
|
||||
echo "Import command (after getting API key):"
|
||||
echo ""
|
||||
for wf in "$WORKFLOWS_DIR"/*.ts; do
|
||||
name=$(basename "$wf" .ts)
|
||||
echo " n8n import:workflow --input=$wf # $name"
|
||||
done
|
||||
71
n8n/wf1_prospects_created.ts
Normal file
71
n8n/wf1_prospects_created.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Daily 02:00', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '0 2 * * *' }] } } } });
|
||||
|
||||
const auth = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Auth CMS BI', parameters: { method: 'POST', url: 'https://aws-prod-auth.cms360.cloud/api/Jwt/FromBody',
|
||||
sendBody: true, bodyParameters: { parameters: [{ name: 'username', value: 'JetourMauritiusBI' }, { name: 'password', value: 'j7PW0ev66mJ2' }] }, options: { timeout: 30000 } } } });
|
||||
|
||||
const setVars = node({ type: 'n8n-nodes-base.set', version: 3.4,
|
||||
config: { name: 'Set Dates + Token', parameters: { mode: 'manual', includeOtherFields: false,
|
||||
assignments: { assignments: [
|
||||
{ id: 't', name: 'token', value: expr('={{ $json.token }}'), type: 'string' },
|
||||
{ id: 'f', name: 'from', value: expr('={{ DateTime.now().minus({ days: 1 }).startOf("day").toISO() }}'), type: 'string' },
|
||||
{ id: 't2', name: 'to', value: expr('={{ DateTime.now().startOf("day").toISO() }}'), type: 'string' }
|
||||
] } } } });
|
||||
|
||||
const apiCall = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Fetch Prospects', parameters: { method: 'POST',
|
||||
url: 'https://bi-api.cmscloud.co.za/api/BusinessIntelligence/Prospects/GetProspectsByDateCreated',
|
||||
sendBody: true, sendHeaders: true, specifyHeaders: 'keypair',
|
||||
headerParameters: { parameters: [
|
||||
{ name: 'Authorization', value: expr('=Bearer {{ $("Set Dates + Token").item.json.token }}') },
|
||||
{ name: 'Content-Type', value: 'application/json' }] },
|
||||
bodyParameters: { parameters: [
|
||||
{ id: 'f', name: 'from', value: expr('={{ $("Set Dates + Token").item.json.from }}') },
|
||||
{ id: 't2', name: 'to', value: expr('={{ $("Set Dates + Token").item.json.to }}') },
|
||||
{ name: 'pageNumber', value: '1' }, { name: 'pageSize', value: '500' }] },
|
||||
options: { timeout: 120000 } } } });
|
||||
|
||||
const flatten = node({ type: 'n8n-nodes-base.code', version: 2,
|
||||
config: { name: 'Flatten', parameters: { mode: 'runOnceForAllItems', jsCode:
|
||||
`const items = $input.all();
|
||||
const ts = new Date().toISOString().replace(/[-:T]/g,'').slice(0,15);
|
||||
const bid = "pr_cr_" + ts;
|
||||
const out = [];
|
||||
function n(v) { return v || null; }
|
||||
for (const it of items) {
|
||||
const d = it.json;
|
||||
out.push({ json: { _t:'P', pid:d.prospectID, cid:d.contactID, cdr:n(d.cmsDealerRef), kci:n(d.keyloopCustID),
|
||||
kca:n(d.keyloopCustAccNumber), dd:n(d.dealerDesc), cn:n(d.companyName), ti:n(d.title),
|
||||
nm:n(d.name), sn:n(d.surname), db:n(d.dtBirth), lg:n(d.language), cl:n(d.cellNumber),
|
||||
em:n(d.emailAddr), sc:n(d.sourceOfInitialContact), rs:n(d.referralSource),
|
||||
sp:n(d.salesPerson), ps:n(d.prospectStatus), lr:n(d.lostReason), bim:n(d.basicInterestMake),
|
||||
bimd:n(d.basicInterestModel), nu:n(d.newUsed), dc:n(d.dtProspectCreated),
|
||||
du:n(d.dtProspectUpdated), da:n(d.dtLastAction), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
if (d.statusLog) for (const s of d.statusLog) {
|
||||
out.push({ json: { _t:'S', pid:d.prospectID, st:s.status, dt:n(s.dtUpdated), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
}
|
||||
}
|
||||
return out;` } } });
|
||||
|
||||
const insertP = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Insert Prospects', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO prospects (prospect_id,contact_id,cms_dealer_ref,keyloop_cust_id,keyloop_cust_acc_number,dealer_desc,company_name,title,name,surname,dt_birth,language,cell_number,email_addr,source_of_initial_contact,referral_source,sales_person,prospect_status,lost_reason,basic_interest_make,basic_interest_model,new_used,dt_prospect_created,dt_prospect_updated,dt_last_action,import_batch_id)
|
||||
VALUES ({{ $json.pid }},{{ $json.cid || 'NULL' }},{{ $json.cdr ? "'"+$json.cdr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.kci ? "'"+$json.kci+"'" : 'NULL' }},{{ $json.kca ? "'"+$json.kca+"'" : 'NULL' }},{{ $json.dd ? "'"+$json.dd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.cn ? "'"+$json.cn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ti ? "'"+$json.ti.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nm ? "'"+$json.nm.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sn ? "'"+$json.sn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.db ? "'"+$json.db+"'::date" : 'NULL' }},{{ $json.lg ? "'"+$json.lg+"'" : 'NULL' }},{{ $json.cl ? "'"+$json.cl+"'" : 'NULL' }},{{ $json.em ? "'"+$json.em+"'" : 'NULL' }},{{ $json.sc ? "'"+$json.sc.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.rs ? "'"+$json.rs.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sp ? "'"+$json.sp.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ps ? "'"+$json.ps.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.lr ? "'"+$json.lr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bim ? "'"+$json.bim.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bimd ? "'"+$json.bimd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nu ? "'"+$json.nu+"'" : 'NULL' }},{{ $json.dc ? "'"+$json.dc+"'::timestamptz" : 'NULL' }},{{ $json.du ? "'"+$json.du+"'::timestamptz" : 'NULL' }},{{ $json.da ? "'"+$json.da+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
const insertS = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Insert Status Logs', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO status_logs (prospect_id,status,dt_updated,import_batch_id)
|
||||
VALUES ((SELECT id FROM prospects WHERE prospect_id={{ $json.pid }}),{{ $json.st ? "'"+$json.st.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.dt ? "'"+$json.dt+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id,status,dt_updated) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
export default workflow('CMS_BI_PROSPECTS_CREATED', 'CMS BI: GetProspectsByDateCreated → jetour-bi')
|
||||
.add(cron).to(auth).to(setVars).to(apiCall).to(flatten)
|
||||
.add(flatten).to(insertP).add(flatten).to(insertS);
|
||||
71
n8n/wf2_prospects_updated.ts
Normal file
71
n8n/wf2_prospects_updated.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Daily 02:10', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '0 10 2 * * *' }] } } } });
|
||||
|
||||
const auth = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Auth CMS BI', parameters: { method: 'POST', url: 'https://aws-prod-auth.cms360.cloud/api/Jwt/FromBody',
|
||||
sendBody: true, bodyParameters: { parameters: [{ name: 'username', value: 'JetourMauritiusBI' }, { name: 'password', value: 'j7PW0ev66mJ2' }] }, options: { timeout: 30000 } } } });
|
||||
|
||||
const setVars = node({ type: 'n8n-nodes-base.set', version: 3.4,
|
||||
config: { name: 'Set Dates + Token', parameters: { mode: 'manual', includeOtherFields: false,
|
||||
assignments: { assignments: [
|
||||
{ id: 't', name: 'token', value: expr('={{ $json.token }}'), type: 'string' },
|
||||
{ id: 'f', name: 'from', value: expr('={{ DateTime.now().minus({ days: 1 }).startOf("day").toISO() }}'), type: 'string' },
|
||||
{ id: 't2', name: 'to', value: expr('={{ DateTime.now().startOf("day").toISO() }}'), type: 'string' }
|
||||
] } } } });
|
||||
|
||||
const apiCall = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Fetch Prospects', parameters: { method: 'POST',
|
||||
url: 'https://bi-api.cmscloud.co.za/api/BusinessIntelligence/Prospects/GetProspectsByDateUpdated',
|
||||
sendBody: true, sendHeaders: true, specifyHeaders: 'keypair',
|
||||
headerParameters: { parameters: [
|
||||
{ name: 'Authorization', value: expr('=Bearer {{ $("Set Dates + Token").item.json.token }}') },
|
||||
{ name: 'Content-Type', value: 'application/json' }] },
|
||||
bodyParameters: { parameters: [
|
||||
{ id: 'f', name: 'from', value: expr('={{ $("Set Dates + Token").item.json.from }}') },
|
||||
{ id: 't2', name: 'to', value: expr('={{ $("Set Dates + Token").item.json.to }}') },
|
||||
{ name: 'pageNumber', value: '1' }, { name: 'pageSize', value: '500' }] },
|
||||
options: { timeout: 120000 } } } });
|
||||
|
||||
const flatten = node({ type: 'n8n-nodes-base.code', version: 2,
|
||||
config: { name: 'Flatten', parameters: { mode: 'runOnceForAllItems', jsCode:
|
||||
`const items = $input.all();
|
||||
const ts = new Date().toISOString().replace(/[-:T]/g,'').slice(0,15);
|
||||
const bid = "pr_upd_" + ts;
|
||||
const out = [];
|
||||
function n(v) { return v || null; }
|
||||
for (const it of items) {
|
||||
const d = it.json;
|
||||
out.push({ json: { _t:'P', pid:d.prospectID, cid:d.contactID, cdr:n(d.cmsDealerRef), kci:n(d.keyloopCustID),
|
||||
kca:n(d.keyloopCustAccNumber), dd:n(d.dealerDesc), cn:n(d.companyName), ti:n(d.title),
|
||||
nm:n(d.name), sn:n(d.surname), db:n(d.dtBirth), lg:n(d.language), cl:n(d.cellNumber),
|
||||
em:n(d.emailAddr), sc:n(d.sourceOfInitialContact), rs:n(d.referralSource),
|
||||
sp:n(d.salesPerson), ps:n(d.prospectStatus), lr:n(d.lostReason), bim:n(d.basicInterestMake),
|
||||
bimd:n(d.basicInterestModel), nu:n(d.newUsed), dc:n(d.dtProspectCreated),
|
||||
du:n(d.dtProspectUpdated), da:n(d.dtLastAction), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
if (d.statusLog) for (const s of d.statusLog) {
|
||||
out.push({ json: { _t:'S', pid:d.prospectID, st:s.status, dt:n(s.dtUpdated), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
}
|
||||
}
|
||||
return out;` } } });
|
||||
|
||||
const insertP = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Insert Prospects', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO prospects (prospect_id,contact_id,cms_dealer_ref,keyloop_cust_id,keyloop_cust_acc_number,dealer_desc,company_name,title,name,surname,dt_birth,language,cell_number,email_addr,source_of_initial_contact,referral_source,sales_person,prospect_status,lost_reason,basic_interest_make,basic_interest_model,new_used,dt_prospect_created,dt_prospect_updated,dt_last_action,import_batch_id)
|
||||
VALUES ({{ $json.pid }},{{ $json.cid || 'NULL' }},{{ $json.cdr ? "'"+$json.cdr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.kci ? "'"+$json.kci+"'" : 'NULL' }},{{ $json.kca ? "'"+$json.kca+"'" : 'NULL' }},{{ $json.dd ? "'"+$json.dd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.cn ? "'"+$json.cn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ti ? "'"+$json.ti.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nm ? "'"+$json.nm.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sn ? "'"+$json.sn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.db ? "'"+$json.db+"'::date" : 'NULL' }},{{ $json.lg ? "'"+$json.lg+"'" : 'NULL' }},{{ $json.cl ? "'"+$json.cl+"'" : 'NULL' }},{{ $json.em ? "'"+$json.em+"'" : 'NULL' }},{{ $json.sc ? "'"+$json.sc.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.rs ? "'"+$json.rs.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sp ? "'"+$json.sp.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ps ? "'"+$json.ps.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.lr ? "'"+$json.lr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bim ? "'"+$json.bim.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bimd ? "'"+$json.bimd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nu ? "'"+$json.nu+"'" : 'NULL' }},{{ $json.dc ? "'"+$json.dc+"'::timestamptz" : 'NULL' }},{{ $json.du ? "'"+$json.du+"'::timestamptz" : 'NULL' }},{{ $json.da ? "'"+$json.da+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id) DO UPDATE SET contact_id=EXCLUDED.contact_id, cms_dealer_ref=EXCLUDED.cms_dealer_ref, dealer_desc=EXCLUDED.dealer_desc, company_name=EXCLUDED.company_name, title=EXCLUDED.title, name=EXCLUDED.name, surname=EXCLUDED.surname, cell_number=EXCLUDED.cell_number, email_addr=EXCLUDED.email_addr, sales_person=EXCLUDED.sales_person, prospect_status=EXCLUDED.prospect_status, lost_reason=EXCLUDED.lost_reason, basic_interest_make=EXCLUDED.basic_interest_make, basic_interest_model=EXCLUDED.basic_interest_model, dt_prospect_updated=EXCLUDED.dt_prospect_updated, dt_last_action=EXCLUDED.dt_last_action, last_imported_at=NOW(), import_batch_id=EXCLUDED.import_batch_id;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
const insertS = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Insert Status Logs', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO status_logs (prospect_id,status,dt_updated,import_batch_id)
|
||||
VALUES ((SELECT id FROM prospects WHERE prospect_id={{ $json.pid }}),{{ $json.st ? "'"+$json.st.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.dt ? "'"+$json.dt+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id,status,dt_updated) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
export default workflow('CMS_BI_PROSPECTS_UPDATED', 'CMS BI: GetProspectsByDateUpdated → jetour-bi')
|
||||
.add(cron).to(auth).to(setVars).to(apiCall).to(flatten)
|
||||
.add(flatten).to(insertP).add(flatten).to(insertS);
|
||||
86
n8n/wf3_otps_created.ts
Normal file
86
n8n/wf3_otps_created.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Daily 02:20', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '20 2 * * *' }] } } } });
|
||||
|
||||
const auth = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Auth CMS BI', parameters: { method: 'POST', url: 'https://aws-prod-auth.cms360.cloud/api/Jwt/FromBody',
|
||||
sendBody: true, bodyParameters: { parameters: [{ name: 'username', value: 'JetourMauritiusBI' }, { name: 'password', value: 'j7PW0ev66mJ2' }] }, options: { timeout: 30000 } } } });
|
||||
|
||||
const setVars = node({ type: 'n8n-nodes-base.set', version: 3.4,
|
||||
config: { name: 'Set Dates + Token', parameters: { mode: 'manual', includeOtherFields: false,
|
||||
assignments: { assignments: [
|
||||
{ id: 't', name: 'token', value: expr('={{ $json.token }}'), type: 'string' },
|
||||
{ id: 'f', name: 'from', value: expr('={{ DateTime.now().minus({ days: 1 }).startOf("day").toISO() }}'), type: 'string' },
|
||||
{ id: 't2', name: 'to', value: expr('={{ DateTime.now().startOf("day").toISO() }}'), type: 'string' }
|
||||
] } } } });
|
||||
|
||||
const apiCall = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Fetch OTPs', parameters: { method: 'POST',
|
||||
url: 'https://bi-api.cmscloud.co.za/api/BusinessIntelligence/Otps/GetOtpsByDateCreated',
|
||||
sendBody: true, sendHeaders: true, specifyHeaders: 'keypair',
|
||||
headerParameters: { parameters: [
|
||||
{ name: 'Authorization', value: expr('=Bearer {{ $("Set Dates + Token").item.json.token }}') },
|
||||
{ name: 'Content-Type', value: 'application/json' }] },
|
||||
bodyParameters: { parameters: [
|
||||
{ id: 'f', name: 'from', value: expr('={{ $("Set Dates + Token").item.json.from }}') },
|
||||
{ id: 't2', name: 'to', value: expr('={{ $("Set Dates + Token").item.json.to }}') },
|
||||
{ name: 'pageNumber', value: '1' }, { name: 'pageSize', value: '500' }] },
|
||||
options: { timeout: 120000 } } } });
|
||||
|
||||
const flatten = node({ type: 'n8n-nodes-base.code', version: 2,
|
||||
config: { name: 'Flatten', parameters: { mode: 'runOnceForAllItems', jsCode:
|
||||
`const items = $input.all();
|
||||
const ts = new Date().toISOString().replace(/[-:T]/g,'').slice(0,15);
|
||||
const bid = "ot_cr_" + ts;
|
||||
const out = [];
|
||||
function n(v) { return v || null; }
|
||||
function pu(v) { if(!v) return null; return v.replace(/[{}]/g,'')||null; }
|
||||
for (const it of items) {
|
||||
const d = it.json;
|
||||
out.push({ json: { _t:'P', pid:d.prospectID, cid:d.contactID, cdr:n(d.cmsDealerRef), kci:n(d.keyloopCustID),
|
||||
kca:n(d.keyloopCustAccNumber), dd:n(d.dealerDesc), cn:n(d.companyName), ti:n(d.title),
|
||||
nm:n(d.name), sn:n(d.surname), db:n(d.dtBirth), lg:n(d.language), cl:n(d.cellNumber),
|
||||
em:n(d.emailAddr), sc:n(d.sourceOfInitialContact), rs:n(d.referralSource),
|
||||
sp:n(d.salesPerson), ps:n(d.prospectStatus), lr:n(d.lostReason), bim:n(d.basicInterestMake),
|
||||
bimd:n(d.basicInterestModel), nu:n(d.newUsed), dc:n(d.dtProspectCreated),
|
||||
du:n(d.dtProspectUpdated), da:n(d.dtLastAction), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
if (d.prospectOTPS) for (const o of d.prospectOTPS) {
|
||||
out.push({ json: { _t:'O', pid:d.prospectID, oid:o.cmsotpid, cpid:o.cmsProspectID,
|
||||
eg:pu(o.exportGUID), on:n(o.otpNewUsed), mc:n(o.mmCode), mk:n(o.make), md:n(o.model),
|
||||
mr:n(o.modelRange), co:n(o.colour), my:n(o.modelYear), vi:n(o.vin), sn:n(o.stockNo),
|
||||
am:o.amount, dc:n(o.dtotpCreated), ds:n(o.dtSold), di:n(o.dtInvoiced), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
}
|
||||
if (d.statusLog) for (const s of d.statusLog) {
|
||||
out.push({ json: { _t:'S', pid:d.prospectID, st:s.status, dt:n(s.dtUpdated), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
}
|
||||
}
|
||||
return out;` } } });
|
||||
|
||||
const insertP = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Upsert Prospects', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO prospects (prospect_id,contact_id,cms_dealer_ref,keyloop_cust_id,keyloop_cust_acc_number,dealer_desc,company_name,title,name,surname,dt_birth,language,cell_number,email_addr,source_of_initial_contact,referral_source,sales_person,prospect_status,lost_reason,basic_interest_make,basic_interest_model,new_used,dt_prospect_created,dt_prospect_updated,dt_last_action,import_batch_id)
|
||||
VALUES ({{ $json.pid }},{{ $json.cid || 'NULL' }},{{ $json.cdr ? "'"+$json.cdr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.kci ? "'"+$json.kci+"'" : 'NULL' }},{{ $json.kca ? "'"+$json.kca+"'" : 'NULL' }},{{ $json.dd ? "'"+$json.dd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.cn ? "'"+$json.cn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ti ? "'"+$json.ti.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nm ? "'"+$json.nm.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sn ? "'"+$json.sn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.db ? "'"+$json.db+"'::date" : 'NULL' }},{{ $json.lg ? "'"+$json.lg+"'" : 'NULL' }},{{ $json.cl ? "'"+$json.cl+"'" : 'NULL' }},{{ $json.em ? "'"+$json.em+"'" : 'NULL' }},{{ $json.sc ? "'"+$json.sc.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.rs ? "'"+$json.rs.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sp ? "'"+$json.sp.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ps ? "'"+$json.ps.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.lr ? "'"+$json.lr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bim ? "'"+$json.bim.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bimd ? "'"+$json.bimd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nu ? "'"+$json.nu+"'" : 'NULL' }},{{ $json.dc ? "'"+$json.dc+"'::timestamptz" : 'NULL' }},{{ $json.du ? "'"+$json.du+"'::timestamptz" : 'NULL' }},{{ $json.da ? "'"+$json.da+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
const insertO = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Upsert OTPs', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO prospect_otps (prospect_id,cms_otp_id,cms_prospect_id,export_guid,otp_new_used,mm_code,make,model,model_range,colour,model_year,vin,stock_no,amount,dt_otp_created,dt_sold,dt_invoiced,import_batch_id)
|
||||
VALUES ((SELECT id FROM prospects WHERE prospect_id={{ $json.pid }}),{{ $json.oid }},{{ $json.cpid }},{{ $json.eg ? "'"+$json.eg+"'::uuid" : 'NULL' }},{{ $json.on ? "'"+$json.on+"'" : 'NULL' }},{{ $json.mc ? "'"+$json.mc+"'" : 'NULL' }},{{ $json.mk ? "'"+$json.mk.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.md ? "'"+$json.md.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.mr ? "'"+$json.mr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.co ? "'"+$json.co.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.my ? "'"+$json.my+"'" : 'NULL' }},{{ $json.vi ? "'"+$json.vi+"'" : 'NULL' }},{{ $json.sn ? "'"+$json.sn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.am !== null && $json.am !== undefined ? $json.am : 'NULL' }},{{ $json.dc ? "'"+$json.dc+"'::timestamptz" : 'NULL' }},{{ $json.ds ? "'"+$json.ds+"'::timestamptz" : 'NULL' }},{{ $json.di ? "'"+$json.di+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (cms_otp_id) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
const insertS = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Upsert Status Logs', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO status_logs (prospect_id,status,dt_updated,import_batch_id)
|
||||
VALUES ((SELECT id FROM prospects WHERE prospect_id={{ $json.pid }}),{{ $json.st ? "'"+$json.st.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.dt ? "'"+$json.dt+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id,status,dt_updated) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
export default workflow('CMS_BI_OTPS_CREATED', 'CMS BI: GetOtpsByDateCreated → jetour-bi')
|
||||
.add(cron).to(auth).to(setVars).to(apiCall).to(flatten)
|
||||
.add(flatten).to(insertP).add(flatten).to(insertO).add(flatten).to(insertS);
|
||||
86
n8n/wf4_otps_updated.ts
Normal file
86
n8n/wf4_otps_updated.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Daily 02:50', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '50 2 * * *' }] } } } });
|
||||
|
||||
const auth = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Auth CMS BI', parameters: { method: 'POST', url: 'https://aws-prod-auth.cms360.cloud/api/Jwt/FromBody',
|
||||
sendBody: true, bodyParameters: { parameters: [{ name: 'username', value: 'JetourMauritiusBI' }, { name: 'password', value: 'j7PW0ev66mJ2' }] }, options: { timeout: 30000 } } } });
|
||||
|
||||
const setVars = node({ type: 'n8n-nodes-base.set', version: 3.4,
|
||||
config: { name: 'Set Dates + Token', parameters: { mode: 'manual', includeOtherFields: false,
|
||||
assignments: { assignments: [
|
||||
{ id: 't', name: 'token', value: expr('={{ $json.token }}'), type: 'string' },
|
||||
{ id: 'f', name: 'from', value: expr('={{ DateTime.now().minus({ days: 1 }).startOf("day").toISO() }}'), type: 'string' },
|
||||
{ id: 't2', name: 'to', value: expr('={{ DateTime.now().startOf("day").toISO() }}'), type: 'string' }
|
||||
] } } } });
|
||||
|
||||
const apiCall = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Fetch OTPs', parameters: { method: 'POST',
|
||||
url: 'https://bi-api.cmscloud.co.za/api/BusinessIntelligence/Otps/GetOtpsByDateUpdated',
|
||||
sendBody: true, sendHeaders: true, specifyHeaders: 'keypair',
|
||||
headerParameters: { parameters: [
|
||||
{ name: 'Authorization', value: expr('=Bearer {{ $("Set Dates + Token").item.json.token }}') },
|
||||
{ name: 'Content-Type', value: 'application/json' }] },
|
||||
bodyParameters: { parameters: [
|
||||
{ id: 'f', name: 'from', value: expr('={{ $("Set Dates + Token").item.json.from }}') },
|
||||
{ id: 't2', name: 'to', value: expr('={{ $("Set Dates + Token").item.json.to }}') },
|
||||
{ name: 'pageNumber', value: '1' }, { name: 'pageSize', value: '500' }] },
|
||||
options: { timeout: 120000 } } } });
|
||||
|
||||
const flatten = node({ type: 'n8n-nodes-base.code', version: 2,
|
||||
config: { name: 'Flatten', parameters: { mode: 'runOnceForAllItems', jsCode:
|
||||
`const items = $input.all();
|
||||
const ts = new Date().toISOString().replace(/[-:T]/g,'').slice(0,15);
|
||||
const bid = "ot_upd_" + ts;
|
||||
const out = [];
|
||||
function n(v) { return v || null; }
|
||||
function pu(v) { if(!v) return null; return v.replace(/[{}]/g,'')||null; }
|
||||
for (const it of items) {
|
||||
const d = it.json;
|
||||
out.push({ json: { _t:'P', pid:d.prospectID, cid:d.contactID, cdr:n(d.cmsDealerRef), kci:n(d.keyloopCustID),
|
||||
kca:n(d.keyloopCustAccNumber), dd:n(d.dealerDesc), cn:n(d.companyName), ti:n(d.title),
|
||||
nm:n(d.name), sn:n(d.surname), db:n(d.dtBirth), lg:n(d.language), cl:n(d.cellNumber),
|
||||
em:n(d.emailAddr), sc:n(d.sourceOfInitialContact), rs:n(d.referralSource),
|
||||
sp:n(d.salesPerson), ps:n(d.prospectStatus), lr:n(d.lostReason), bim:n(d.basicInterestMake),
|
||||
bimd:n(d.basicInterestModel), nu:n(d.newUsed), dc:n(d.dtProspectCreated),
|
||||
du:n(d.dtProspectUpdated), da:n(d.dtLastAction), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
if (d.prospectOTPS) for (const o of d.prospectOTPS) {
|
||||
out.push({ json: { _t:'O', pid:d.prospectID, oid:o.cmsotpid, cpid:o.cmsProspectID,
|
||||
eg:pu(o.exportGUID), on:n(o.otpNewUsed), mc:n(o.mmCode), mk:n(o.make), md:n(o.model),
|
||||
mr:n(o.modelRange), co:n(o.colour), my:n(o.modelYear), vi:n(o.vin), sn:n(o.stockNo),
|
||||
am:o.amount, dc:n(o.dtotpCreated), ds:n(o.dtSold), di:n(o.dtInvoiced), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
}
|
||||
if (d.statusLog) for (const s of d.statusLog) {
|
||||
out.push({ json: { _t:'S', pid:d.prospectID, st:s.status, dt:n(s.dtUpdated), bid:bid },
|
||||
pairedItem: { item: it.index } });
|
||||
}
|
||||
}
|
||||
return out;` } } });
|
||||
|
||||
const insertP = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Upsert Prospects', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO prospects (prospect_id,contact_id,cms_dealer_ref,keyloop_cust_id,keyloop_cust_acc_number,dealer_desc,company_name,title,name,surname,dt_birth,language,cell_number,email_addr,source_of_initial_contact,referral_source,sales_person,prospect_status,lost_reason,basic_interest_make,basic_interest_model,new_used,dt_prospect_created,dt_prospect_updated,dt_last_action,import_batch_id)
|
||||
VALUES ({{ $json.pid }},{{ $json.cid || 'NULL' }},{{ $json.cdr ? "'"+$json.cdr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.kci ? "'"+$json.kci+"'" : 'NULL' }},{{ $json.kca ? "'"+$json.kca+"'" : 'NULL' }},{{ $json.dd ? "'"+$json.dd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.cn ? "'"+$json.cn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ti ? "'"+$json.ti.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nm ? "'"+$json.nm.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sn ? "'"+$json.sn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.db ? "'"+$json.db+"'::date" : 'NULL' }},{{ $json.lg ? "'"+$json.lg+"'" : 'NULL' }},{{ $json.cl ? "'"+$json.cl+"'" : 'NULL' }},{{ $json.em ? "'"+$json.em+"'" : 'NULL' }},{{ $json.sc ? "'"+$json.sc.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.rs ? "'"+$json.rs.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.sp ? "'"+$json.sp.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.ps ? "'"+$json.ps.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.lr ? "'"+$json.lr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bim ? "'"+$json.bim.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.bimd ? "'"+$json.bimd.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.nu ? "'"+$json.nu+"'" : 'NULL' }},{{ $json.dc ? "'"+$json.dc+"'::timestamptz" : 'NULL' }},{{ $json.du ? "'"+$json.du+"'::timestamptz" : 'NULL' }},{{ $json.da ? "'"+$json.da+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
const insertO = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Upsert OTPs', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO prospect_otps (prospect_id,cms_otp_id,cms_prospect_id,export_guid,otp_new_used,mm_code,make,model,model_range,colour,model_year,vin,stock_no,amount,dt_otp_created,dt_sold,dt_invoiced,import_batch_id)
|
||||
VALUES ((SELECT id FROM prospects WHERE prospect_id={{ $json.pid }}),{{ $json.oid }},{{ $json.cpid }},{{ $json.eg ? "'"+$json.eg+"'::uuid" : 'NULL' }},{{ $json.on ? "'"+$json.on+"'" : 'NULL' }},{{ $json.mc ? "'"+$json.mc+"'" : 'NULL' }},{{ $json.mk ? "'"+$json.mk.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.md ? "'"+$json.md.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.mr ? "'"+$json.mr.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.co ? "'"+$json.co.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.my ? "'"+$json.my+"'" : 'NULL' }},{{ $json.vi ? "'"+$json.vi+"'" : 'NULL' }},{{ $json.sn ? "'"+$json.sn.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.am !== null && $json.am !== undefined ? $json.am : 'NULL' }},{{ $json.dc ? "'"+$json.dc+"'::timestamptz" : 'NULL' }},{{ $json.ds ? "'"+$json.ds+"'::timestamptz" : 'NULL' }},{{ $json.di ? "'"+$json.di+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (cms_otp_id) DO UPDATE SET prospect_id=EXCLUDED.prospect_id, cms_prospect_id=EXCLUDED.cms_prospect_id, export_guid=EXCLUDED.export_guid, otp_new_used=EXCLUDED.otp_new_used, mm_code=EXCLUDED.mm_code, make=EXCLUDED.make, model=EXCLUDED.model, model_range=EXCLUDED.model_range, colour=EXCLUDED.colour, model_year=EXCLUDED.model_year, vin=EXCLUDED.vin, stock_no=EXCLUDED.stock_no, amount=EXCLUDED.amount, dt_sold=EXCLUDED.dt_sold, dt_invoiced=EXCLUDED.dt_invoiced, last_imported_at=NOW(), import_batch_id=EXCLUDED.import_batch_id;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
const insertS = node({ type: 'n8n-nodes-base.postgres', version: 2.4,
|
||||
config: { name: 'Upsert Status Logs', parameters: { operation: 'executeQuery', query:
|
||||
expr(`=INSERT INTO status_logs (prospect_id,status,dt_updated,import_batch_id)
|
||||
VALUES ((SELECT id FROM prospects WHERE prospect_id={{ $json.pid }}),{{ $json.st ? "'"+$json.st.replace(/'/g,"''")+"'" : 'NULL' }},{{ $json.dt ? "'"+$json.dt+"'::timestamptz" : 'NULL' }},{{ "'"+$json.bid+"'" }})
|
||||
ON CONFLICT (prospect_id,status,dt_updated) DO NOTHING;`) },
|
||||
credentials: { postgres: { name: 'jetour-bi-remote' } } } });
|
||||
|
||||
export default workflow('CMS_BI_OTPS_UPDATED', 'CMS BI: GetOtpsByDateUpdated → jetour-bi')
|
||||
.add(cron).to(auth).to(setVars).to(apiCall).to(flatten)
|
||||
.add(flatten).to(insertP).add(flatten).to(insertO).add(flatten).to(insertS);
|
||||
14
n8n/wf_email_daily.ts
Normal file
14
n8n/wf_email_daily.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Daily 05:00 UTC', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '0 5 * * *' }] } } } });
|
||||
|
||||
const sendReport = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Send Daily Report', parameters: {
|
||||
method: 'POST', url: 'http://100.91.25.139:8765/api/send-report/daily',
|
||||
sendBody: false, sendHeaders: false,
|
||||
options: { timeout: 60000, redirect: { redirect: {} }, response: { response: { responseFormat: 'json' } } }
|
||||
} } });
|
||||
|
||||
export default workflow('CMS_BI_EMAIL_DAILY', '📧 CMS BI: Daily Email Report')
|
||||
.add(cron).to(sendReport);
|
||||
14
n8n/wf_email_monthly.ts
Normal file
14
n8n/wf_email_monthly.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Monthly 1st 05:00 UTC', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '0 5 1 * *' }] } } } });
|
||||
|
||||
const sendReport = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Send Monthly Report', parameters: {
|
||||
method: 'POST', url: 'http://100.91.25.139:8765/api/send-report/monthly',
|
||||
sendBody: false, sendHeaders: false,
|
||||
options: { timeout: 60000, redirect: { redirect: {} }, response: { response: { responseFormat: 'json' } } }
|
||||
} } });
|
||||
|
||||
export default workflow('CMS_BI_EMAIL_MONTHLY', '📧 CMS BI: Monthly Email Report')
|
||||
.add(cron).to(sendReport);
|
||||
14
n8n/wf_email_weekly.ts
Normal file
14
n8n/wf_email_weekly.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { workflow, node, trigger, expr } from '@n8n/workflow-sdk';
|
||||
|
||||
const cron = trigger({ type: 'n8n-nodes-base.scheduleTrigger', version: 1.3,
|
||||
config: { name: 'Weekly Mon 05:00 UTC', parameters: { rule: { interval: [{ field: 'cronExpression', expression: '0 5 * * 1' }] } } } });
|
||||
|
||||
const sendReport = node({ type: 'n8n-nodes-base.httpRequest', version: 4.4,
|
||||
config: { name: 'Send Weekly Report', parameters: {
|
||||
method: 'POST', url: 'http://100.91.25.139:8765/api/send-report/weekly',
|
||||
sendBody: false, sendHeaders: false,
|
||||
options: { timeout: 60000, redirect: { redirect: {} }, response: { response: { responseFormat: 'json' } } }
|
||||
} } });
|
||||
|
||||
export default workflow('CMS_BI_EMAIL_WEEKLY', '📧 CMS BI: Weekly Email Report')
|
||||
.add(cron).to(sendReport);
|
||||
Reference in New Issue
Block a user