postiz + n8n: real DB URL + webhook-trigger approval
- postiz: set DATABASE_URL/REDIS_URL pointing at the bundled subcharts; the chart does NOT auto-wire even when postgresql.enabled=true, so the prisma db:push was failing with empty DATABASE_URL. - n8n approval workflow: swap telegramTrigger -> webhook node so it works without an n8n-stored Telegram credential. Telegram bot's webhook is set via setWebhook to https://n8n.viktorbarzin.me/webhook/instagram-approval. Parse-callback Code node tolerates both shapes ({body:{callback_query:...}} vs {callback_query:...}) so a future move back to telegramTrigger doesn't break.
This commit is contained in:
parent
5057341d09
commit
c6939c3d53
2 changed files with 275 additions and 78 deletions
|
|
@ -6,32 +6,34 @@
|
|||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"updates": ["callback_query"],
|
||||
"additionalFields": {}
|
||||
"httpMethod": "POST",
|
||||
"path": "instagram-approval",
|
||||
"responseMode": "onReceived",
|
||||
"options": {}
|
||||
},
|
||||
"id": "telegram-trigger",
|
||||
"name": "Telegram callback_query",
|
||||
"type": "n8n-nodes-base.telegramTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [250, 400],
|
||||
"name": "Telegram Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
250,
|
||||
400
|
||||
],
|
||||
"webhookId": "f2c7c254-ebaf-4f66-b1b4-5c1629c07e08",
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "telegram-bot",
|
||||
"name": "Telegram Bot"
|
||||
}
|
||||
},
|
||||
"notes": "Listens for inline-button taps. Requires a Telegram credential bound to the same bot whose token is in TELEGRAM_BOT_TOKEN."
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const cb = $input.first().json.callback_query || {};\nconst data = cb.data || '';\nconst [action, assetId] = data.split(':');\nconst message = cb.message || {};\nconst chatId = (message.chat || {}).id;\nconst messageId = message.message_id;\nconst originalCaption = message.caption || '';\nconst callbackQueryId = cb.id;\n\nif (!action || !assetId) {\n throw new Error('Malformed callback_data: ' + data);\n}\n\nreturn [{\n json: {\n action,\n asset_id: assetId,\n chat_id: chatId,\n message_id: messageId,\n original_caption: originalCaption,\n callback_query_id: callbackQueryId\n }\n}];"
|
||||
"jsCode": "// Webhook puts Telegram update under .body; Telegram trigger puts it at root\nconst raw = $input.first().json;\nconst update = raw.body || raw;\nconst cb = update.callback_query || {};\nconst data = cb.data || '';\nconst [action, assetId] = data.split(':');\nconst message = cb.message || {};\nconst chatId = (message.chat || {}).id;\nconst messageId = message.message_id;\nconst originalCaption = message.caption || '';\nconst callbackQueryId = cb.id;\nreturn [{\n json: {\n action,\n asset_id: assetId,\n chat_id: chatId,\n message_id: messageId,\n original_caption: originalCaption,\n callback_query_id: callbackQueryId,\n }\n}];"
|
||||
},
|
||||
"id": "parse-callback",
|
||||
"name": "Parse callback_data",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [470, 400],
|
||||
"position": [
|
||||
470,
|
||||
400
|
||||
],
|
||||
"notes": "Splits callback_data into action + asset_id, captures chat_id/message_id/caption for later edits and answerCallbackQuery."
|
||||
},
|
||||
{
|
||||
|
|
@ -40,16 +42,44 @@
|
|||
"values": [
|
||||
{
|
||||
"conditions": {
|
||||
"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"},
|
||||
"conditions": [{"id": "is-approve", "leftValue": "={{ $json.action }}", "rightValue": "approve", "operator": {"type": "string", "operation": "equals"}}],
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "is-approve",
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "approve",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"outputKey": "approve"
|
||||
},
|
||||
{
|
||||
"conditions": {
|
||||
"options": {"caseSensitive": true, "leftValue": "", "typeValidation": "strict"},
|
||||
"conditions": [{"id": "is-reject", "leftValue": "={{ $json.action }}", "rightValue": "reject", "operator": {"type": "string", "operation": "equals"}}],
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "is-reject",
|
||||
"leftValue": "={{ $json.action }}",
|
||||
"rightValue": "reject",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"outputKey": "reject"
|
||||
|
|
@ -62,7 +92,10 @@
|
|||
"name": "Switch on action",
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"typeVersion": 3.2,
|
||||
"position": [690, 400],
|
||||
"position": [
|
||||
690,
|
||||
400
|
||||
],
|
||||
"notes": "Branches on action; unknown actions fall through and are dropped."
|
||||
},
|
||||
{
|
||||
|
|
@ -72,20 +105,31 @@
|
|||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Authorization", "value": "=Bearer {{ $env.INSTAGRAM_POSTER_TOKEN }}"},
|
||||
{"name": "Content-Type", "value": "application/json"}
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $env.INSTAGRAM_POSTER_TOKEN }}"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ asset_id: $json.asset_id }) }}",
|
||||
"options": {"timeout": 30000}
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"id": "approve-enqueue",
|
||||
"name": "Approve: enqueue asset",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [910, 250],
|
||||
"position": [
|
||||
910,
|
||||
250
|
||||
],
|
||||
"onError": "continueErrorOutput",
|
||||
"notes": "Calls instagram-poster /enqueue. continueErrorOutput so we can fall through to a Telegram error message on failure."
|
||||
},
|
||||
|
|
@ -96,20 +140,31 @@
|
|||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Authorization", "value": "=Bearer {{ $env.INSTAGRAM_POSTER_TOKEN }}"},
|
||||
{"name": "Content-Type", "value": "application/json"}
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $env.INSTAGRAM_POSTER_TOKEN }}"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ asset_id: $json.asset_id }) }}",
|
||||
"options": {"timeout": 30000}
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"id": "reject-mark",
|
||||
"name": "Reject: mark seen",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [910, 550],
|
||||
"position": [
|
||||
910,
|
||||
550
|
||||
],
|
||||
"onError": "continueErrorOutput",
|
||||
"notes": "Calls instagram-poster /reject so the asset is recorded and not re-surfaced."
|
||||
},
|
||||
|
|
@ -121,7 +176,10 @@
|
|||
"name": "Approve: build new caption",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1130, 250],
|
||||
"position": [
|
||||
1130,
|
||||
250
|
||||
],
|
||||
"notes": "Append a confirmation suffix to the original caption."
|
||||
},
|
||||
{
|
||||
|
|
@ -132,7 +190,10 @@
|
|||
"name": "Reject: build new caption",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1130, 550],
|
||||
"position": [
|
||||
1130,
|
||||
550
|
||||
],
|
||||
"notes": "Append rejection suffix to the original caption."
|
||||
},
|
||||
{
|
||||
|
|
@ -142,19 +203,27 @@
|
|||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Content-Type", "value": "application/json"}
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chat_id, message_id: $json.message_id, caption: $json.new_caption, parse_mode: 'HTML' }) }}",
|
||||
"options": {"timeout": 30000}
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"id": "edit-caption",
|
||||
"name": "Telegram editMessageCaption",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1350, 400],
|
||||
"position": [
|
||||
1350,
|
||||
400
|
||||
],
|
||||
"notes": "Updates the original DM caption to show the resulting state. Strips inline buttons in the same call by omitting reply_markup combined with the next node."
|
||||
},
|
||||
{
|
||||
|
|
@ -164,19 +233,27 @@
|
|||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Content-Type", "value": "application/json"}
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chat_id, message_id: $json.message_id, reply_markup: { inline_keyboard: [] } }) }}",
|
||||
"options": {"timeout": 30000}
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"id": "edit-reply-markup",
|
||||
"name": "Telegram editMessageReplyMarkup",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1570, 400],
|
||||
"position": [
|
||||
1570,
|
||||
400
|
||||
],
|
||||
"notes": "Strips the inline approve/reject buttons so the original DM no longer offers them after a decision."
|
||||
},
|
||||
{
|
||||
|
|
@ -186,19 +263,27 @@
|
|||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Content-Type", "value": "application/json"}
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ callback_query_id: $json.callback_query_id, text: 'Recorded' }) }}",
|
||||
"options": {"timeout": 15000}
|
||||
"options": {
|
||||
"timeout": 15000
|
||||
}
|
||||
},
|
||||
"id": "answer-callback",
|
||||
"name": "Telegram answerCallbackQuery",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1790, 400],
|
||||
"position": [
|
||||
1790,
|
||||
400
|
||||
],
|
||||
"notes": "Dismisses the loading spinner on the user's tap."
|
||||
},
|
||||
{
|
||||
|
|
@ -209,7 +294,10 @@
|
|||
"name": "Build error message",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1130, 750],
|
||||
"position": [
|
||||
1130,
|
||||
750
|
||||
],
|
||||
"notes": "Catches non-2xx from /enqueue or /reject and formats a Telegram alert text."
|
||||
},
|
||||
{
|
||||
|
|
@ -219,51 +307,171 @@
|
|||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Content-Type", "value": "application/json"}
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chat_id, text: $json.text }) }}",
|
||||
"options": {"timeout": 15000}
|
||||
"options": {
|
||||
"timeout": 15000
|
||||
}
|
||||
},
|
||||
"id": "telegram-error-msg",
|
||||
"name": "Telegram error notice",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1350, 750],
|
||||
"position": [
|
||||
1350,
|
||||
750
|
||||
],
|
||||
"notes": "Sends the error text to the user as a follow-up message."
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Telegram callback_query": {"main": [[{"node": "Parse callback_data", "type": "main", "index": 0}]]},
|
||||
"Parse callback_data": {"main": [[{"node": "Switch on action", "type": "main", "index": 0}]]},
|
||||
"Parse callback_data": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Switch on action",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Switch on action": {
|
||||
"main": [
|
||||
[{"node": "Approve: enqueue asset", "type": "main", "index": 0}],
|
||||
[{"node": "Reject: mark seen", "type": "main", "index": 0}]
|
||||
[
|
||||
{
|
||||
"node": "Approve: enqueue asset",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Reject: mark seen",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Approve: enqueue asset": {
|
||||
"main": [
|
||||
[{"node": "Approve: build new caption", "type": "main", "index": 0}],
|
||||
[{"node": "Build error message", "type": "main", "index": 0}]
|
||||
[
|
||||
{
|
||||
"node": "Approve: build new caption",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Build error message",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Reject: mark seen": {
|
||||
"main": [
|
||||
[{"node": "Reject: build new caption", "type": "main", "index": 0}],
|
||||
[{"node": "Build error message", "type": "main", "index": 0}]
|
||||
[
|
||||
{
|
||||
"node": "Reject: build new caption",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Build error message",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Approve: build new caption": {"main": [[{"node": "Telegram editMessageCaption", "type": "main", "index": 0}]]},
|
||||
"Reject: build new caption": {"main": [[{"node": "Telegram editMessageCaption", "type": "main", "index": 0}]]},
|
||||
"Telegram editMessageCaption": {"main": [[{"node": "Telegram editMessageReplyMarkup", "type": "main", "index": 0}]]},
|
||||
"Telegram editMessageReplyMarkup": {"main": [[{"node": "Telegram answerCallbackQuery", "type": "main", "index": 0}]]},
|
||||
"Build error message": {"main": [[{"node": "Telegram error notice", "type": "main", "index": 0}]]}
|
||||
"Approve: build new caption": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Telegram editMessageCaption",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Reject: build new caption": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Telegram editMessageCaption",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Telegram editMessageCaption": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Telegram editMessageReplyMarkup",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Telegram editMessageReplyMarkup": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Telegram answerCallbackQuery",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Build error message": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Telegram error notice",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Telegram Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse callback_data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1",
|
||||
"saveExecutionProgress": false,
|
||||
"saveManualExecutions": true
|
||||
},
|
||||
"settings": {"executionOrder": "v1", "saveExecutionProgress": false, "saveManualExecutions": true},
|
||||
"staticData": null,
|
||||
"meta": {"templateCredsSetupCompleted": false},
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": false
|
||||
},
|
||||
"pinData": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,27 +134,16 @@ resource "helm_release" "postiz" {
|
|||
NX_ADD_PLUGINS = "false"
|
||||
}
|
||||
|
||||
# Empty placeholder for chart-rendered Secret. ESO patches JWT_SECRET via
|
||||
# creationPolicy=Merge above. DATABASE_URL/REDIS_URL are auto-wired by the
|
||||
# chart's bundled subcharts and don't need to be set here.
|
||||
# Postiz reads DATABASE_URL/REDIS_URL from this Secret. The chart does
|
||||
# NOT auto-wire bundled subcharts — we have to point at the in-namespace
|
||||
# PG/Redis Services. ESO patches JWT_SECRET on top via creationPolicy=Merge.
|
||||
# Subchart auth uses the chart defaults (postiz / postiz-password,
|
||||
# postiz-redis-password) — both Services are ClusterIP, only routable
|
||||
# from inside the postiz namespace, so the well-known creds are safe.
|
||||
secrets = {
|
||||
DATABASE_URL = ""
|
||||
REDIS_URL = ""
|
||||
JWT_SECRET = ""
|
||||
X_API_KEY = ""
|
||||
X_API_SECRET = ""
|
||||
LINKEDIN_CLIENT_ID = ""
|
||||
LINKEDIN_CLIENT_SECRET = ""
|
||||
REDDIT_CLIENT_ID = ""
|
||||
REDDIT_CLIENT_SECRET = ""
|
||||
GITHUB_CLIENT_ID = ""
|
||||
GITHUB_CLIENT_SECRET = ""
|
||||
RESEND_API_KEY = ""
|
||||
CLOUDFLARE_ACCOUNT_ID = ""
|
||||
CLOUDFLARE_ACCESS_KEY = ""
|
||||
CLOUDFLARE_SECRET_ACCESS_KEY = ""
|
||||
CLOUDFLARE_BUCKETNAME = ""
|
||||
CLOUDFLARE_BUCKET_URL = ""
|
||||
DATABASE_URL = "postgresql://postiz:postiz-password@postiz-postgresql:5432/postiz"
|
||||
REDIS_URL = "redis://default:postiz-redis-password@postiz-redis-master:6379"
|
||||
JWT_SECRET = ""
|
||||
}
|
||||
|
||||
# Use our PVC for uploads (overrides the chart's emptyDir default).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue