chore: remove dashboard nodes and credentials

This commit is contained in:
Jesse Freitas 2026-03-12 13:16:21 -03:00
parent 54b7dd6ea1
commit 2a8fb1376d
8 changed files with 76 additions and 1333 deletions

238
README.md
View file

@ -5,9 +5,7 @@ Node de comunidade para [n8n](https://n8n.io) para trabalhar com a API do Mega.
## Recursos
- `Mega` node regular para APIs do Mega com escopo de conta
- `Mega Client` node regular para APIs Client públicas
- `Mega Dashboard App` node regular para gerar recursos de app embutido servido pelo n8n
- `Mega Dashboard Script` node regular para gerar scripts de dashboard injetados por URL
- `Mega Client` node regular para APIs Client públicas
- `Mega Platform` node regular para APIs Platform do Mega
- `Account -> Get` operation
- `Account -> Update` operation
@ -31,18 +29,16 @@ Node de comunidade para [n8n](https://n8n.io) para trabalhar com a API do Mega.
- `Team -> Get Many`, `Get`, `Create`, `Update`, `Delete`, `Get Agents`, `Add Agent`, `Remove Agent`, and `Update Agents` operations
- `Webhook -> Get Many`, `Create`, `Update`, and `Delete` operations
- `Mega API` credential with `Base URL`, `API Access Token`, and `Mega Account ID`
- `Mega Dashboard App` credential with `Base App URL`, `Shared Secret`, and `Allowed Mega Origin`
- `Mega Dashboard Script API` credential with `Base Script URL`, `Default Iframe URL`, and `Allowed Mega Origin`
- `Mega Client API` credential with `Base URL` and `Inbox Identifier`
- `Mega Platform API` credential with `Base URL` and `Platform API Access Token`
- Teste de conexão da credencial usando `GET /api/v1/profile`
- Teste de conexão da credencial usando `GET /api/v1/profile`
## Requisitos
- Node.js 22+ é necessário para executar os comandos atuais de `build` e `lint` do `@n8n/node-cli`
- Node.js 22+ é necessário para executar os comandos atuais de `build` e `lint` do `@n8n/node-cli`
- npm
## Instalação
## Instalação
```bash
npm install
@ -65,8 +61,8 @@ npm install @jessefreitas/n8n-nodes-mega
Crie uma credencial `Mega API` no n8n com:
- `Base URL`: URL da sua instância Mega, por exemplo `https://app.example.com`
- `API Access Token`: token da aplicação enviado no header `api_access_token`
- `Base URL`: URL da sua instância Mega, por exemplo `https://app.example.com`
- `API Access Token`: token da aplicação enviado no header `api_access_token`
- `Mega Account ID`: identificador externo da conta Mega usado em endpoints com escopo de conta
O teste da credencial chama `GET /api/v1/profile` para validar o token.
@ -81,7 +77,7 @@ Use o node `Mega Platform` com a credencial `Mega Platform API` para endpoints d
Crie uma credencial `Mega Platform API` no n8n com:
- `Base URL`: URL da sua instância Mega
- `Base URL`: URL da sua instância Mega
- `Platform API Access Token`: token do app platform enviado no header `api_access_token`
O teste da credencial chama:
@ -99,13 +95,13 @@ Recursos suportados em `Mega Platform`:
Importante:
- `Mega` and `Mega Platform` não compartilham credenciais
- `Mega` é para APIs de aplicação com escopo de conta em `/api/v1/accounts/*`
- `Mega Platform` é para APIs Platform em `/platform/api/v1/*`
- `Mega` and `Mega Platform` não compartilham credenciais
- `Mega` é para APIs de aplicação com escopo de conta em `/api/v1/accounts/*`
- `Mega Platform` é para APIs Platform em `/platform/api/v1/*`
## API Client
Use o node `Mega Client` com a credencial `Mega Client API` para endpoints públicos client em:
Use o node `Mega Client` com a credencial `Mega Client API` para endpoints públicos client em:
```text
/public/api/v1/*
@ -113,8 +109,8 @@ Use o node `Mega Client` com a credencial `Mega Client API` para endpoints públ
Crie uma credencial `Mega Client API` no n8n com:
- `Base URL`: URL da sua instância Mega
- `Inbox Identifier`: identificador público da caixa de entrada usado pelas APIs Client
- `Base URL`: URL da sua instância Mega
- `Inbox Identifier`: identificador público da caixa de entrada usado pelas APIs Client
Recursos suportados em `Mega Client`:
@ -125,63 +121,16 @@ Recursos suportados em `Mega Client`:
Importante:
- `Mega`, `Mega Platform`, and `Mega Client` não compartilham credenciais
- `Mega Client` usa identificadores públicos como `inbox_identifier`, `contact_identifier`, and `conversation_id`
- `CSAT Survey` usa uma rota pública `conversation_uuid` fora do padrão `/public/api/v1/inboxes/*`
- `Mega`, `Mega Platform`, and `Mega Client` não compartilham credenciais
- `Mega Client` usa identificadores públicos como `inbox_identifier`, `contact_identifier`, and `conversation_id`
- `CSAT Survey` usa uma rota pública `conversation_uuid` fora do padrão `/public/api/v1/inboxes/*`
## Mega Dashboard App
Use o node `Mega Dashboard App` com a credencial `Mega Dashboard App` para gerar os recursos necessários para um app embutido dentro do dashboard do Mega.
Crie uma credencial `Mega Dashboard App` no n8n com:
- `Base App URL`: URL pública que servirá o app embutido
- `Shared Secret`: segredo usado pelo app embutido ao chamar o n8n de volta
- `Allowed Mega Origin`: origem esperada do Mega permitida para enviar contexto via `postMessage`
- `App Name`: nome padrão opcional do app para a configuração gerada
- `App Icon URL`: URL opcional do ícone para a configuração gerada
Operações suportadas em `Mega Dashboard App`:
- `Generate Config`
- `Generate Context Bridge`
- `Generate Embed Page`
- `Verify Request`
Importante:
- `Mega Dashboard App` não grava configurações no Mega automaticamente
- o app é registrado manualmente no Mega usando a configuração gerada
- o app embutido deve ser servido por um webhook do n8n ou outra URL pública sob seu controle
- injeção de script de dashboard está fora do escopo deste node
## Mega Dashboard Script
Use o node `Mega Dashboard Script` com a credencial `Mega Dashboard Script API` para gerar o JavaScript que o Mega carrega por URL na área de Dashboard Scripts.
Crie uma credencial `Mega Dashboard Script API` no n8n com:
- `Base Script URL`: URL pública que vai servir o JavaScript injetado
- `Default Iframe URL`: URL padrão aberta dentro do painel embutido
- `Allowed Mega Origin`: origem esperada do Mega que pode executar o script
Operações suportadas em `Mega Dashboard Script`:
- `Generate Config`
- `Generate Script`
Importante:
- este node é para o caso em que o Mega espera um link de script para injeção
- o script gerado adiciona um item na sidebar e abre um iframe dentro do painel do Mega
- o script tenta manter a sidebar visível, acompanha o tema claro/escuro e pode fechar o painel ao clicar em outro item da sidebar
- o JavaScript deve ser servido com `content-type: application/javascript`
## Operações
## Operações
### Account -> Get
Obtém detalhes da conta em:
Obtém detalhes da conta em:
```text
GET /api/v1/accounts/{accountId}
@ -202,60 +151,25 @@ Suporta atualizar estes campos:
- `Company Size`
- `Timezone`
O node envia requisições para:
O node envia requisições para:
```text
PATCH /api/v1/accounts/{accountId}
```
## Mega Dashboard App Operations
O node `Mega Dashboard App` ajuda a preparar um app de dashboard servido pelo n8n e registrado manualmente no Mega.
Saídas geradas:
- `Generate Config`: returns app metadata, iframe URL, allowed origin, shared secret, and manual setup steps
- `Generate Context Bridge`: returns the browser JavaScript that receives Mega context via `postMessage` and forwards actions to an n8n webhook
- `Generate Embed Page`: returns HTML ready for an `HTTP Response` node or another public endpoint
- `Verify Request`: validates request origin and shared secret against the credential
Fluxo recomendado:
1. Crie um webhook público no n8n que servirá a página embutida.
2. Use `Generate Embed Page` para produzir o HTML retornado por esse webhook.
3. Use `Generate Config` para coletar a URL do iframe e os valores de registro no Mega.
4. Registre o app manualmente no Mega.
5. Use `Verify Request` no fluxo de backend antes de processar ações sensíveis.
## Mega Dashboard Script Operations
O node `Mega Dashboard Script` ajuda a gerar um script injetável por URL para a área de Dashboard Scripts do Mega.
Saídas geradas:
- `Generate Config`: retorna a URL pública do script, um loader inline opcional, a configuração final do script e os passos manuais
- `Generate Script`: retorna o JavaScript completo com `content_type` igual a `application/javascript; charset=utf-8`
Fluxo recomendado:
1. Crie um webhook público no n8n que vai responder com o JavaScript do script.
2. Use `Generate Script` para obter o JavaScript e devolva esse conteúdo no webhook com `application/javascript`.
3. Use `Generate Config` para obter a URL do script e o loader inline opcional.
4. No Mega, configure o link do Dashboard Script apontando para a URL pública do script ou cole o loader inline, se necessário.
5. Recarregue o dashboard do Mega e valide se o botão aparece na posição escolhida.
## Mega Platform Operations
### Platform Account
Operações suportadas:
Operações suportadas:
- `Create`
- `Get`
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
POST /platform/api/v1/accounts
@ -276,13 +190,13 @@ Campos suportados:
### Platform Account User
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /platform/api/v1/accounts/{accountId}/account_users
@ -298,7 +212,7 @@ Campos suportados:
### Platform Agent Bot
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -306,7 +220,7 @@ Operações suportadas:
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /platform/api/v1/agent_bots
@ -328,7 +242,7 @@ The documented binary `avatar` upload field is not implemented yet in this node.
### Platform User
Operações suportadas:
Operações suportadas:
- `Create`
- `Get`
@ -336,7 +250,7 @@ Operações suportadas:
- `Delete`
- `Get SSO Link`
O node envia requisições para:
O node envia requisições para:
```text
POST /platform/api/v1/users
@ -358,13 +272,13 @@ Campos suportados:
### Client Contact
Operações suportadas:
Operações suportadas:
- `Create`
- `Get`
- `Update`
O node envia requisições para:
O node envia requisições para:
```text
POST /public/api/v1/inboxes/{inboxIdentifier}/contacts
@ -386,7 +300,7 @@ The documented binary `avatar` upload field is not implemented yet in this node.
### Client Conversation
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -395,7 +309,7 @@ Operações suportadas:
- `Toggle Typing Status`
- `Update Last Seen`
O node envia requisições para:
O node envia requisições para:
```text
GET /public/api/v1/inboxes/{inboxIdentifier}/contacts/{contactIdentifier}/conversations
@ -415,13 +329,13 @@ Campos suportados:
### Client Message
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Update`
O node envia requisições para:
O node envia requisições para:
```text
GET /public/api/v1/inboxes/{inboxIdentifier}/contacts/{contactIdentifier}/conversations/{conversationId}/messages
@ -440,11 +354,11 @@ Campos suportados:
### Client CSAT Survey
Operações suportadas:
Operações suportadas:
- `Get`
O node envia requisições para:
O node envia requisições para:
```text
GET /survey/responses/{conversationUuid}
@ -466,11 +380,11 @@ Available fields:
- `Page`
Este endpoint só está disponível em edições Enterprise do Mega com o recurso de logs de auditoria habilitado.
Este endpoint só está disponível em edições Enterprise do Mega com o recurso de logs de auditoria habilitado.
### Agent Bot
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -478,7 +392,7 @@ Operações suportadas:
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/agent_bots
@ -501,14 +415,14 @@ The documented binary `avatar` upload field is not implemented yet in this node.
### Agent
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/agents
@ -527,7 +441,7 @@ Campos suportados:
### Automation Rule
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -535,7 +449,7 @@ Operações suportadas:
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/automation_rules
@ -556,11 +470,11 @@ Campos suportados:
### Campaign
Operações suportadas:
Operações suportadas:
- `Create`
O node envia requisições para:
O node envia requisições para:
```text
POST /api/v1/accounts/{accountId}/campaigns
@ -580,14 +494,14 @@ This first implementation covers only campaign creation and models `audience` as
### Canned Response
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/canned_responses
@ -603,7 +517,7 @@ Campos suportados:
### Chat Room
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -618,7 +532,7 @@ Operações suportadas:
- `Get Messages`
- `Create Message`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/chat_rooms
@ -652,7 +566,7 @@ This first implementation uses JSON payloads only. Binary avatar uploads for roo
### Custom Filter
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -660,7 +574,7 @@ Operações suportadas:
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/custom_filters
@ -679,7 +593,7 @@ Campos suportados:
### Contact
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -699,7 +613,7 @@ Operações suportadas:
- `Set Labels`
- `Merge`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/contacts
@ -745,13 +659,13 @@ The documented binary `avatar` upload field is not implemented yet in this node.
### Portal
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Update`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/portals
@ -773,11 +687,11 @@ Campos suportados:
### Portal Category
Operações suportadas:
Operações suportadas:
- `Create`
O node envia requisições para:
O node envia requisições para:
```text
POST /api/v1/accounts/{accountId}/portals/{id}/categories
@ -796,11 +710,11 @@ Campos suportados:
### Portal Article
Operações suportadas:
Operações suportadas:
- `Create`
O node envia requisições para:
O node envia requisições para:
```text
POST /api/v1/accounts/{accountId}/portals/{id}/articles
@ -822,7 +736,7 @@ Campos suportados:
### Custom Attribute
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
@ -830,7 +744,7 @@ Operações suportadas:
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/custom_attribute_definitions
@ -854,7 +768,7 @@ Campos suportados:
### Inbox
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Get`
@ -867,7 +781,7 @@ Operações suportadas:
- `Remove Agent`
- `Update Agents`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/inboxes
@ -907,14 +821,14 @@ The documented binary `avatar` upload field is not implemented yet in this node.
### Integration
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/integrations/hooks
@ -932,13 +846,13 @@ Campos suportados:
### Message
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/conversations/{conversationId}/messages
@ -961,7 +875,7 @@ Campos suportados:
### Scheduled Message
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Get`
@ -975,7 +889,7 @@ The node supports two scopes:
- `Account`
- `Conversation`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/scheduled_messages
@ -1022,7 +936,7 @@ GET /api/v1/profile
### Team
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Get`
@ -1034,7 +948,7 @@ Operações suportadas:
- `Remove Agent`
- `Update Agents`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/teams
@ -1057,14 +971,14 @@ Campos suportados:
### Webhook
Operações suportadas:
Operações suportadas:
- `Get Many`
- `Create`
- `Update`
- `Delete`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/webhooks
@ -1081,7 +995,7 @@ Campos suportados:
### Conversation
Operações suportadas:
Operações suportadas:
- `Get Counts`
- `Get Many`
@ -1100,7 +1014,7 @@ Operações suportadas:
- `Get Reporting Events`
- `Assign`
O node envia requisições para:
O node envia requisições para:
```text
GET /api/v1/accounts/{accountId}/conversations/meta
@ -1145,11 +1059,11 @@ Campos suportados:
- `Typing Status`
- `Private Note`
## Validação local
## Validação local
```bash
npm run lint
npm run build
```
Se você estiver usando uma versão mais antiga do Node.js, o `@n8n/node-cli` atual pode falhar durante a validação local. Use Node.js 22+ antes de publicar ou submeter o pacote para revisão.
Se você estiver usando uma versão mais antiga do Node.js, o `@n8n/node-cli` atual pode falhar durante a validação local. Use Node.js 22+ antes de publicar ou submeter o pacote para revisão.

View file

@ -1,62 +0,0 @@
import type { Icon, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
export class MegaDashboardAppApi implements ICredentialType {
name = 'megaDashboardAppApi';
displayName = 'Mega Dashboard App API';
documentationUrl = 'https://github.com/jessefreitas/n8n_community_mega?tab=readme-ov-file#mega-dashboard-app';
icon: Icon = { light: 'file:../icons/mega.svg', dark: 'file:../icons/mega.dark.svg' };
properties: INodeProperties[] = [
{
displayName: 'URL Base do App',
name: 'baseAppUrl',
type: 'string',
default: '',
placeholder: 'https://n8n.example.com/webhook/mega-dashboard-app',
required: true,
description: 'URL pública que servirá o app embutido do dashboard',
},
{
displayName: 'Segredo Compartilhado',
name: 'sharedSecret',
type: 'string',
typeOptions: { password: true },
default: '',
required: true,
description: 'Segredo compartilhado usado entre o app embutido e os webhooks do n8n',
},
{
displayName: 'Origem Mega Permitida',
name: 'allowedMegaOrigin',
type: 'string',
default: '',
placeholder: 'https://chat.example.com',
required: true,
description: 'Origem do Mega permitida para enviar contexto do dashboard via postMessage',
},
{
displayName: 'Nome do App',
name: 'appName',
type: 'string',
default: 'Mega Dashboard App',
description: 'Nome padrão exibido nas saídas de configuração geradas',
},
{
displayName: 'URL do Ícone do App',
name: 'appIconUrl',
type: 'string',
default: '',
description: 'URL opcional do ícone usada nas saídas de configuração geradas',
},
];
test: ICredentialTestRequest = {
request: {
url: '={{$credentials.baseAppUrl}}',
method: 'GET',
},
};
}

View file

@ -1,49 +0,0 @@
import type { Icon, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
export class MegaDashboardScriptApi implements ICredentialType {
name = 'megaDashboardScriptApi';
displayName = 'Mega Dashboard Script API';
documentationUrl =
'https://github.com/jessefreitas/n8n_community_mega?tab=readme-ov-file#mega-dashboard-script';
icon: Icon = { light: 'file:../icons/mega.svg', dark: 'file:../icons/mega.dark.svg' };
properties: INodeProperties[] = [
{
displayName: 'Base Script URL',
name: 'baseScriptUrl',
type: 'string',
default: '',
placeholder: 'https://n8n.example.com/webhook/mega-dashboard-script',
required: true,
description: 'Public URL that will serve the injected dashboard script',
},
{
displayName: 'Default Iframe URL',
name: 'defaultIframeUrl',
type: 'string',
default: '',
placeholder: 'https://n8n.example.com/webhook/mega-dashboard-app',
required: true,
description: 'Default URL opened inside the embedded dashboard panel',
},
{
displayName: 'Allowed Mega Origin',
name: 'allowedMegaOrigin',
type: 'string',
default: '',
placeholder: 'https://chat.example.com',
required: true,
description: 'Mega origin allowed to execute the injected dashboard script',
},
];
test: ICredentialTestRequest = {
request: {
url: '={{$credentials.baseScriptUrl}}',
method: 'GET',
},
};
}

View file

@ -1,18 +0,0 @@
{
"node": "n8n-nodes-mega.megaDashboardApp",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Communication"],
"resources": {
"credentialDocumentation": [
{
"url": "https://github.com/jessefreitas/n8n_community_mega?tab=readme-ov-file#mega-dashboard-app"
}
],
"primaryDocumentation": [
{
"url": "https://github.com/jessefreitas/n8n_community_mega?tab=readme-ov-file"
}
]
}
}

View file

@ -1,530 +0,0 @@
import type {
IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
function normalizeOrigin(value: string): string {
const trimmed = value.trim();
const match = trimmed.match(/^(https?:\/\/[^/]+)/i);
return (match?.[1] || trimmed).replace(/\/+$/, '');
}
function escapeHtml(value: string): string {
return value
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function buildContextBridgeScript(
allowedMegaOrigin: string,
backendWebhookUrl: string,
sharedSecret: string,
): string {
return `(() => {
const CFG = {
allowedMegaOrigin: ${JSON.stringify(allowedMegaOrigin)},
backendWebhookUrl: ${JSON.stringify(backendWebhookUrl)},
sharedSecret: ${JSON.stringify(sharedSecret)},
};
const state = { context: null };
const normalizeOrigin = (value) => {
try {
return new URL(value).origin;
} catch {
return String(value || '').trim().replace(/\\/+$/, '');
}
};
const emitContext = () => {
window.dispatchEvent(new CustomEvent('mega-dashboard-context', { detail: state.context }));
};
window.megaDashboardApp = {
getContext() {
return state.context;
},
async send(payload = {}) {
const response = await fetch(CFG.backendWebhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Mega-Shared-Secret': CFG.sharedSecret,
'X-Mega-Source': 'dashboard-app',
},
body: JSON.stringify({
context: state.context,
payload,
}),
});
const text = await response.text();
try {
return JSON.parse(text);
} catch {
return {
ok: response.ok,
status: response.status,
raw: text,
};
}
},
};
window.addEventListener('message', (event) => {
if (
CFG.allowedMegaOrigin &&
normalizeOrigin(event.origin) !== normalizeOrigin(CFG.allowedMegaOrigin)
) {
return;
}
state.context = event.data || null;
emitContext();
});
if (window.parent && window.parent !== window) {
window.parent.postMessage({ type: 'mega-dashboard-app:ready' }, '*');
}
})();`;
}
function buildEmbedPageHtml(
pageTitle: string,
pageHeading: string,
loadingText: string,
emptyStateText: string,
bridgeScript: string,
): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${escapeHtml(pageTitle)}</title>
<style>
:root {
color-scheme: light dark;
--bg: #f4f1ea;
--panel: #fffdf9;
--text: #18222f;
--muted: #5f6b78;
--border: #d8d2c7;
--accent: #0f766e;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #10161d;
--panel: #16212b;
--text: #edf3f8;
--muted: #9fb0bf;
--border: #293746;
--accent: #4fd1c5;
}
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 24px;
font-family: Georgia, "Times New Roman", serif;
background: linear-gradient(180deg, var(--bg), var(--panel));
color: var(--text);
}
.wrap {
max-width: 960px;
margin: 0 auto;
display: grid;
gap: 16px;
}
.card {
border: 1px solid var(--border);
border-radius: 14px;
background: color-mix(in srgb, var(--panel) 92%, transparent);
padding: 18px;
}
h1 {
margin: 0 0 8px;
font-size: 28px;
line-height: 1.1;
}
p {
margin: 0;
color: var(--muted);
}
pre {
margin: 0;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
font-size: 13px;
line-height: 1.5;
}
.pill {
display: inline-block;
padding: 6px 10px;
border-radius: 999px;
background: color-mix(in srgb, var(--accent) 12%, transparent);
color: var(--accent);
font: 600 12px/1.2 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<span class="pill">Mega Dashboard App</span>
<h1>${escapeHtml(pageHeading)}</h1>
<p id="mega-dashboard-status">${escapeHtml(loadingText)}</p>
</div>
<div class="card">
<pre id="mega-dashboard-context">${escapeHtml(emptyStateText)}</pre>
</div>
</div>
<script>${bridgeScript}</script>
<script>
(() => {
const status = document.getElementById('mega-dashboard-status');
const contextPre = document.getElementById('mega-dashboard-context');
const emptyState = ${JSON.stringify(emptyStateText)};
const render = () => {
const context = window.megaDashboardApp?.getContext?.();
if (!context) {
status.textContent = ${JSON.stringify(loadingText)};
contextPre.textContent = emptyState;
return;
}
status.textContent = 'Context received from Mega';
contextPre.textContent = JSON.stringify(context, null, 2);
};
window.addEventListener('mega-dashboard-context', render);
render();
})();
</script>
</body>
</html>`;
}
export class MegaDashboardApp implements INodeType {
description: INodeTypeDescription = {
displayName: 'Mega Dashboard App',
name: 'megaDashboardApp',
icon: { light: 'file:../../icons/mega.svg', dark: 'file:../../icons/mega.dark.svg' },
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"]}}',
description: 'Generate and verify assets for a Mega dashboard app served by n8n',
defaults: {
name: 'Mega Dashboard App',
},
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
usableAsTool: true,
credentials: [
{
name: 'megaDashboardAppApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Generate Config',
value: 'generateConfig',
description: 'Generate config for manual mega registration',
action: 'Generate config for manual mega registration',
},
{
name: 'Generate Context Bridge',
value: 'generateContextBridge',
description: 'Generate context bridge script for the embedded app',
action: 'Generate context bridge script for the embedded app',
},
{
name: 'Generate Embed Page',
value: 'generateEmbedPage',
description: 'Generate html page for an n8n webhook response',
action: 'Generate html page for an n8n webhook response',
},
{
name: 'Verify Request',
value: 'verifyRequest',
description: 'Verify request origin and shared secret',
action: 'Verify request origin and shared secret',
},
],
default: 'generateConfig',
},
{
displayName: 'App Name',
name: 'dashboardAppName',
type: 'string',
default: '',
description: 'Optional name override for the generated dashboard app config',
displayOptions: {
show: {
operation: ['generateConfig'],
},
},
},
{
displayName: 'Sidebar Label',
name: 'dashboardSidebarLabel',
type: 'string',
default: '',
description: 'Optional sidebar label shown in the Mega dashboard',
displayOptions: {
show: {
operation: ['generateConfig'],
},
},
},
{
displayName: 'App Description',
name: 'dashboardAppDescription',
type: 'string',
typeOptions: {
rows: 3,
},
default: '',
description: 'Optional description used in generated app config output',
displayOptions: {
show: {
operation: ['generateConfig'],
},
},
},
{
displayName: 'Iframe URL',
name: 'dashboardIframeUrl',
type: 'string',
default: '',
description: 'Optional iframe URL override. Leave empty to use the credential Base App URL.',
displayOptions: {
show: {
operation: ['generateConfig'],
},
},
},
{
displayName: 'Backend Webhook URL',
name: 'dashboardBackendWebhookUrl',
type: 'string',
default: '',
required: true,
description: 'Webhook URL the embedded app should call back to in n8n',
displayOptions: {
show: {
operation: ['generateContextBridge', 'generateEmbedPage'],
},
},
},
{
displayName: 'Page Title',
name: 'dashboardPageTitle',
type: 'string',
default: 'Mega Dashboard App',
description: 'Title used in the generated HTML page',
displayOptions: {
show: {
operation: ['generateEmbedPage'],
},
},
},
{
displayName: 'Page Heading',
name: 'dashboardPageHeading',
type: 'string',
default: 'Mega Dashboard App',
description: 'Heading shown in the generated HTML page',
displayOptions: {
show: {
operation: ['generateEmbedPage'],
},
},
},
{
displayName: 'Loading Text',
name: 'dashboardLoadingText',
type: 'string',
default: 'Waiting for context from Mega...',
description: 'Loading message shown before dashboard context is received',
displayOptions: {
show: {
operation: ['generateEmbedPage'],
},
},
},
{
displayName: 'Empty State Text',
name: 'dashboardEmptyStateText',
type: 'string',
typeOptions: {
rows: 3,
},
default: 'No dashboard context received yet.',
description: 'Fallback text shown until context arrives',
displayOptions: {
show: {
operation: ['generateEmbedPage'],
},
},
},
{
displayName: 'Request Origin',
name: 'verifyRequestOrigin',
type: 'string',
default: '',
required: true,
description: 'Origin of the incoming request or postMessage event',
displayOptions: {
show: {
operation: ['verifyRequest'],
},
},
},
{
displayName: 'Provided Secret',
name: 'verifyProvidedSecret',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
description: 'Shared secret received from the embedded app request',
displayOptions: {
show: {
operation: ['verifyRequest'],
},
},
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const credentials = await this.getCredentials('megaDashboardAppApi');
const baseAppUrl = credentials.baseAppUrl as string;
const sharedSecret = credentials.sharedSecret as string;
const allowedMegaOrigin = normalizeOrigin(credentials.allowedMegaOrigin as string);
const defaultAppName = ((credentials.appName as string) || 'Mega Dashboard App').trim();
const defaultAppIconUrl = ((credentials.appIconUrl as string) || '').trim();
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
const operation = this.getNodeParameter('operation', itemIndex) as string;
let response: IDataObject;
if (operation === 'generateConfig') {
const appName = (
(this.getNodeParameter('dashboardAppName', itemIndex, '') as string) || defaultAppName
).trim();
const sidebarLabel = (
(this.getNodeParameter('dashboardSidebarLabel', itemIndex, '') as string) || appName
).trim();
const appDescription = this.getNodeParameter(
'dashboardAppDescription',
itemIndex,
'',
) as string;
const iframeUrl = (
(this.getNodeParameter('dashboardIframeUrl', itemIndex, '') as string) || baseAppUrl
).trim();
response = {
app_name: appName,
sidebar_label: sidebarLabel,
iframe_url: iframeUrl,
allowed_mega_origin: allowedMegaOrigin,
shared_secret: sharedSecret,
app_icon_url: defaultAppIconUrl || undefined,
description: appDescription || undefined,
manual_setup_steps: [
'Open the Mega dashboard app configuration screen.',
'Create a new dashboard app entry.',
'Paste the generated iframe URL and sidebar label.',
'Restrict the app to the allowed Mega origin.',
'Use the shared secret in n8n webhook verification.',
],
};
} else if (operation === 'generateContextBridge') {
const backendWebhookUrl = this.getNodeParameter(
'dashboardBackendWebhookUrl',
itemIndex,
) as string;
response = {
allowed_mega_origin: allowedMegaOrigin,
backend_webhook_url: backendWebhookUrl,
javascript: buildContextBridgeScript(
allowedMegaOrigin,
backendWebhookUrl,
sharedSecret,
),
};
} else if (operation === 'generateEmbedPage') {
const backendWebhookUrl = this.getNodeParameter(
'dashboardBackendWebhookUrl',
itemIndex,
) as string;
const pageTitle = this.getNodeParameter('dashboardPageTitle', itemIndex) as string;
const pageHeading = this.getNodeParameter('dashboardPageHeading', itemIndex) as string;
const loadingText = this.getNodeParameter('dashboardLoadingText', itemIndex) as string;
const emptyStateText = this.getNodeParameter(
'dashboardEmptyStateText',
itemIndex,
) as string;
const bridgeScript = buildContextBridgeScript(
allowedMegaOrigin,
backendWebhookUrl,
sharedSecret,
);
response = {
content_type: 'text/html; charset=utf-8',
html: buildEmbedPageHtml(
pageTitle,
pageHeading,
loadingText,
emptyStateText,
bridgeScript,
),
};
} else {
const requestOrigin = normalizeOrigin(
this.getNodeParameter('verifyRequestOrigin', itemIndex) as string,
);
const providedSecret = this.getNodeParameter(
'verifyProvidedSecret',
itemIndex,
) as string;
const originValid = requestOrigin === allowedMegaOrigin;
const secretValid = providedSecret === sharedSecret;
response = {
valid: originValid && secretValid,
origin_valid: originValid,
secret_valid: secretValid,
expected_origin: allowedMegaOrigin,
received_origin: requestOrigin,
issues: [
...(originValid ? [] : ['Origin does not match the allowed Mega origin']),
...(secretValid ? [] : ['Shared secret does not match']),
],
};
}
returnData.push({
json: response,
pairedItem: itemIndex,
});
}
return [returnData];
}
}

View file

@ -1,18 +0,0 @@
{
"node": "n8n-nodes-mega.megaDashboardScript",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Communication"],
"resources": {
"credentialDocumentation": [
{
"url": "https://github.com/jessefreitas/n8n_community_mega?tab=readme-ov-file#mega-dashboard-script"
}
],
"primaryDocumentation": [
{
"url": "https://github.com/jessefreitas/n8n_community_mega?tab=readme-ov-file"
}
]
}
}

View file

@ -1,490 +0,0 @@
import type {
IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
function normalizeOrigin(value: string): string {
const trimmed = value.trim();
const match = trimmed.match(/^(https?:\/\/[^/]+)/i);
return (match?.[1] || trimmed).replace(/\/+$/, '');
}
function buildDashboardScript(config: {
allowedMegaOrigin: string;
autoHideOnNavigation: boolean;
iframeUrl: string;
linkIcon: string;
linkText: string;
padding: number;
positionIndex: number;
positionLabel: string;
positionMode: string;
showBorder: boolean;
}): string {
return `(() => {
const CFG = ${JSON.stringify(config, null, 2)};
const $ = (selector, root = document) => {
try {
return root.querySelector(selector);
} catch {
return null;
}
};
const $$ = (selector, root = document) => {
try {
return Array.from(root.querySelectorAll(selector));
} catch {
return [];
}
};
const normalize = (value) => String(value || '').toLowerCase().replace(/\\s+/g, ' ').trim();
const normalizeOrigin = (value) => {
try {
return new URL(value).origin.replace(/\\/+$/, '');
} catch {
return String(value || '').trim().replace(/\\/+$/, '');
}
};
if (
CFG.allowedMegaOrigin &&
normalizeOrigin(window.location.origin) !== normalizeOrigin(CFG.allowedMegaOrigin)
) {
return;
}
function findSidebar() {
return $('[data-testid="sidebar-primary"]') || $('aside');
}
function findAnyList() {
const sidebar = findSidebar();
if (!sidebar) return null;
return $('ul', sidebar);
}
function findListAndReferenceByLabel(labelWanted) {
const sidebar = findSidebar();
if (!sidebar) return { list: null, ref: null };
const wanted = normalize(labelWanted);
const lists = $$('ul', sidebar);
for (const list of lists) {
const items = $$(':scope > li', list);
for (const item of items) {
const text = normalize(item.textContent);
if (text === wanted || text.includes(wanted)) {
return { list, ref: item };
}
}
}
return { list: null, ref: null };
}
function setLinkActive(active) {
const link = $('#mega-dashboard-script-link-anchor');
if (!link) return;
link.style.background = active ? 'rgba(15, 118, 110, 0.12)' : 'transparent';
}
function ensurePanel() {
if ($('#mega-dashboard-script-panel')) return $('#mega-dashboard-script-panel');
const panel = document.createElement('div');
panel.id = 'mega-dashboard-script-panel';
panel.style.cssText =
'position:fixed;inset:auto 0 0 0;top:0;z-index:9;display:none;pointer-events:none;';
const frameWrap = document.createElement('div');
frameWrap.id = 'mega-dashboard-script-frame-wrap';
frameWrap.style.cssText =
'height:100%;width:100%;padding:0;pointer-events:auto;background:transparent;';
const iframe = document.createElement('iframe');
iframe.id = 'mega-dashboard-script-iframe';
iframe.setAttribute('title', CFG.linkText || 'Mega Dashboard Script');
iframe.style.cssText = 'width:100%;height:100%;border:0;border-radius:12px;background:transparent;';
frameWrap.appendChild(iframe);
panel.appendChild(frameWrap);
document.body.appendChild(panel);
const applyTheme = () => {
const frameWrapEl = $('#mega-dashboard-script-frame-wrap');
if (!frameWrapEl) return;
const bodyStyles = window.getComputedStyle(document.body);
const borderColor =
bodyStyles.getPropertyValue('--border-color') ||
bodyStyles.getPropertyValue('--color-border') ||
'rgba(24, 34, 47, 0.12)';
frameWrapEl.style.padding = String(Number(CFG.padding) || 0) + 'px';
frameWrapEl.style.background = 'transparent';
frameWrapEl.style.border = CFG.showBorder ? '1px solid ' + borderColor.trim() : '0';
frameWrapEl.style.boxSizing = 'border-box';
};
const layout = () => {
const sidebar = findSidebar();
if (!sidebar) return;
const rect = sidebar.getBoundingClientRect();
panel.style.left = rect.right + 'px';
panel.style.width = Math.max(window.innerWidth - rect.right, 0) + 'px';
panel.style.height = window.innerHeight + 'px';
};
window.__megaDashboardScriptShow = () => {
iframe.src = CFG.iframeUrl;
layout();
applyTheme();
panel.style.display = 'block';
setLinkActive(true);
};
window.__megaDashboardScriptHide = () => {
panel.style.display = 'none';
iframe.src = 'about:blank';
setLinkActive(false);
};
applyTheme();
layout();
window.addEventListener('resize', layout, { passive: true });
window.addEventListener('resize', applyTheme, { passive: true });
new MutationObserver(() => {
applyTheme();
layout();
}).observe(document.body, { attributes: true, childList: true, subtree: true });
return panel;
}
function ensureSidebarLink() {
if ($('#mega-dashboard-script-link')) return;
const item = document.createElement('li');
item.id = 'mega-dashboard-script-link';
item.style.listStyle = 'none';
item.innerHTML = '<a id="mega-dashboard-script-link-anchor" href="javascript:void(0)" style="display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:8px;text-decoration:none;color:inherit;"><span></span><span></span></a>';
const link = $('#mega-dashboard-script-link-anchor', item);
const icon = $('span:first-child', item);
const text = $('span:last-child', item);
if (icon) icon.textContent = CFG.linkIcon || 'M';
if (text) text.textContent = CFG.linkText || 'Mega Script';
let parentList = null;
let reference = null;
if (CFG.positionMode === 'afterLabel' || CFG.positionMode === 'beforeLabel') {
const found = findListAndReferenceByLabel(CFG.positionLabel);
parentList = found.list;
reference = found.ref;
}
if (!parentList) parentList = findAnyList();
if (!parentList) return;
const items = $$(':scope > li', parentList);
switch (CFG.positionMode) {
case 'start':
parentList.insertBefore(item, items[0] || null);
break;
case 'index':
parentList.insertBefore(
item,
items[Math.max(0, Math.min(Number(CFG.positionIndex) || 0, items.length))] || null,
);
break;
case 'afterLabel':
if (reference && reference.nextSibling) parentList.insertBefore(item, reference.nextSibling);
else parentList.appendChild(item);
break;
case 'beforeLabel':
if (reference) parentList.insertBefore(item, reference);
else parentList.insertBefore(item, items[0] || null);
break;
case 'end':
default:
parentList.appendChild(item);
break;
}
if (link) {
link.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const panel = ensurePanel();
if (!panel) return;
if (panel.style.display === 'block') {
window.__megaDashboardScriptHide?.();
} else {
window.__megaDashboardScriptShow?.();
}
});
}
}
function bindSidebarNavigationHide() {
if (!CFG.autoHideOnNavigation) return;
const sidebar = findSidebar();
if (!sidebar || sidebar.dataset.megaDashboardScriptHideBound === '1') return;
sidebar.dataset.megaDashboardScriptHideBound = '1';
sidebar.addEventListener(
'click',
(event) => {
const target = event.target;
const ownLink = $('#mega-dashboard-script-link');
if (ownLink && target instanceof Node && ownLink.contains(target)) return;
window.__megaDashboardScriptHide?.();
},
true,
);
}
function boot() {
ensurePanel();
ensureSidebarLink();
bindSidebarNavigationHide();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot, { once: true });
} else {
boot();
}
new MutationObserver(() => {
ensureSidebarLink();
bindSidebarNavigationHide();
}).observe(document.body, { childList: true, subtree: true });
})();`;
}
export class MegaDashboardScript implements INodeType {
description: INodeTypeDescription = {
displayName: 'Mega Dashboard Script',
name: 'megaDashboardScript',
icon: { light: 'file:../../icons/mega.svg', dark: 'file:../../icons/mega.dark.svg' },
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"]}}',
description: 'Generate an injectable Mega dashboard script for sidebar buttons and embedded panels',
defaults: {
name: 'Mega Dashboard Script',
},
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
usableAsTool: true,
credentials: [
{
name: 'megaDashboardScriptApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Generate Config',
value: 'generateConfig',
description: 'Generate config and setup instructions for a Mega dashboard script',
action: 'Generate config and setup instructions for a mega dashboard script',
},
{
name: 'Generate Script',
value: 'generateScript',
description: 'Generate the injected JavaScript for a Mega dashboard script URL',
action: 'Generate the injected script for a mega dashboard script URL',
},
],
default: 'generateConfig',
},
{
displayName: 'Link Text',
name: 'linkText',
type: 'string',
default: 'Meu Aplicativo',
required: true,
description: 'Visible label used for the sidebar link',
},
{
displayName: 'Link Icon',
name: 'linkIcon',
type: 'string',
default: 'M',
description: 'Short icon text displayed beside the sidebar link label',
},
{
displayName: 'Iframe URL',
name: 'iframeUrl',
type: 'string',
default: '',
description: 'Optional iframe URL override. Leave empty to use the credential Default Iframe URL.',
},
{
displayName: 'Position Mode',
name: 'positionMode',
type: 'options',
options: [
{ name: 'After Label', value: 'afterLabel' },
{ name: 'Before Label', value: 'beforeLabel' },
{ name: 'End', value: 'end' },
{ name: 'Index', value: 'index' },
{ name: 'Start', value: 'start' },
],
default: 'afterLabel',
description: 'How the link should be inserted into the Mega sidebar',
},
{
displayName: 'Position Label',
name: 'positionLabel',
type: 'string',
default: 'Conversas',
description: 'Reference label used by the beforeLabel and afterLabel modes',
displayOptions: {
show: {
positionMode: ['afterLabel', 'beforeLabel'],
},
},
},
{
displayName: 'Position Index',
name: 'positionIndex',
type: 'number',
typeOptions: {
numberPrecision: 0,
},
default: 0,
description: 'Index used when the position mode is index',
displayOptions: {
show: {
positionMode: ['index'],
},
},
},
{
displayName: 'Panel Padding',
name: 'panelPadding',
type: 'number',
default: 0,
description: 'Padding applied around the embedded panel in pixels',
},
{
displayName: 'Show Border',
name: 'showBorder',
type: 'boolean',
default: true,
description: 'Whether to render a border around the embedded panel',
},
{
displayName: 'Auto Hide On Navigation',
name: 'autoHideOnNavigation',
type: 'boolean',
default: true,
description: 'Whether to hide the embedded panel when another sidebar item is clicked',
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const credentials = await this.getCredentials('megaDashboardScriptApi');
const baseScriptUrl = (credentials.baseScriptUrl as string).trim();
const defaultIframeUrl = (credentials.defaultIframeUrl as string).trim();
const allowedMegaOrigin = normalizeOrigin(credentials.allowedMegaOrigin as string);
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
const operation = this.getNodeParameter('operation', itemIndex) as string;
const linkText = (this.getNodeParameter('linkText', itemIndex) as string).trim();
const linkIcon = (this.getNodeParameter('linkIcon', itemIndex) as string).trim();
const iframeUrl =
((this.getNodeParameter('iframeUrl', itemIndex, '') as string) || defaultIframeUrl).trim();
const positionMode = this.getNodeParameter('positionMode', itemIndex) as string;
const positionLabel = (this.getNodeParameter('positionLabel', itemIndex, '') as string).trim();
const positionIndex = this.getNodeParameter('positionIndex', itemIndex, 0) as number;
const panelPadding = this.getNodeParameter('panelPadding', itemIndex, 0) as number;
const showBorder = this.getNodeParameter('showBorder', itemIndex, true) as boolean;
const autoHideOnNavigation = this.getNodeParameter(
'autoHideOnNavigation',
itemIndex,
true,
) as boolean;
const scriptConfig = {
allowedMegaOrigin,
autoHideOnNavigation,
iframeUrl,
linkIcon,
linkText,
padding: panelPadding,
positionIndex,
positionLabel,
positionMode,
showBorder,
};
let response: IDataObject;
if (operation === 'generateScript') {
response = {
content_type: 'application/javascript; charset=utf-8',
javascript: buildDashboardScript(scriptConfig),
script_url: baseScriptUrl,
iframe_url: iframeUrl,
allowed_mega_origin: allowedMegaOrigin,
};
} else {
const inlineLoader = `(() => {
const script = document.createElement('script');
script.src = ${JSON.stringify(baseScriptUrl)};
script.async = true;
document.head.appendChild(script);
})();`;
response = {
script_url: baseScriptUrl,
iframe_url: iframeUrl,
allowed_mega_origin: allowedMegaOrigin,
inline_loader_javascript: inlineLoader,
script_config: scriptConfig,
manual_setup_steps: [
'Create a public n8n webhook that returns the generated JavaScript with content-type application/javascript.',
'Use the Base Script URL in the Mega dashboard script configuration.',
'If Mega expects inline code instead of a URL, use the generated inline loader JavaScript.',
'Point the iframe URL to the app or page that should open inside the Mega panel.',
'Reload the Mega dashboard and confirm the sidebar link appears in the configured position.',
],
};
}
returnData.push({ json: response });
}
return [returnData];
}
}

View file

@ -38,14 +38,10 @@
"strict": true,
"credentials": [
"dist/credentials/MegaApi.credentials.js",
"dist/credentials/MegaDashboardAppApi.credentials.js",
"dist/credentials/MegaDashboardScriptApi.credentials.js",
"dist/credentials/MegaClientApi.credentials.js",
"dist/credentials/MegaPlatformApi.credentials.js"
],
"nodes": [
"dist/nodes/MegaDashboardApp/MegaDashboardApp.node.js",
"dist/nodes/MegaDashboardScript/MegaDashboardScript.node.js",
"dist/nodes/Mega/Mega.node.js",
"dist/nodes/MegaClient/MegaClient.node.js",
"dist/nodes/MegaPlatform/MegaPlatform.node.js"