Plataforma de concierge digital para turismo no litoral do Piauí, com frontend React autenticado via Supabase, backend Fastify para orquestração segura e n8n como motor de automação e IA.
- Visão geral
- Arquitetura atual
- Fluxo do chat
- Estrutura do repositório
- Stack
- Ambiente local
- Variáveis de ambiente
- Contrato entre backend e n8n
- Persistência do histórico
- Deploy e operação
- Documentação complementar
O projeto foi organizado para separar claramente as responsabilidades:
apps/frontend: aplicação web do clienteapps/backend: API autenticada para chat, SSE, histórico e integração com n8nsupabase/: assets relacionados ao Supabasedocs/: documentação de arquitetura e diagramas operacionais
O fluxo atual prioriza:
- autenticação e contexto do usuário no frontend e backend
- n8n como camada de automação e IA, sem expor segredos ao cliente
- persistência do histórico oficial no Supabase
- experiência de chat resiliente com cache local + reidratação remota
Frontend React
|
| POST /api/chat/messages
| GET /api/chat/events/:sessionId (SSE)
| GET /api/chat/history/:sessionId
v
Backend Fastify
|
| valida auth no Supabase
| carrega user_profiles
| persiste chat_sessions/chat_messages
| chama webhook do n8n
v
n8n
|
| processa mensagem
| usa Prompt Builder / Agent / Tools
| faz callback assíncrono
v
Backend Fastify
|
| POST /api/chat/callback
| publica SSE para o cliente
| persiste reply da assistente
v
Frontend React
- autenticação do usuário via Supabase
- UI do chat
- cache local de sessão e transcript
- assinatura do stream SSE
- recuperação do histórico persistido
- validação de bearer token
- montagem de
userInfoa partir deauth.users+user_profiles - criação e isolamento de sessão por usuário
- orquestração do fluxo assíncrono com n8n
- persistência oficial do histórico no Supabase
- automação do atendimento
- montagem de prompt
- execução do modelo
- uso futuro de tools, como Google Places
- callback para o backend com
processing,replyeerror
- o frontend autenticado envia
messageesessionIdparaPOST /api/chat/messages - o backend valida o token e monta
userInfo - o backend registra a sessão no event bus e persiste a mensagem do usuário
- o backend dispara o webhook do n8n
- o backend devolve
202 Acceptedimediatamente - o frontend abre SSE em
GET /api/chat/events/:sessionId - o n8n processa e chama
POST /api/chat/callback - o backend publica eventos
buffering,processing,replyouerror - o frontend atualiza a UI em tempo real
GET /healthzPOST /api/chat- rota legada síncrona
POST /api/chat/messages- rota principal do fluxo assíncrono
GET /api/chat/events/:sessionId- SSE do atendimento
GET /api/chat/history/:sessionId- histórico persistido da conversa
POST /api/chat/callback- callback autenticado do n8n
concicerge/
├── apps/
│ ├── backend/
│ │ ├── src/
│ │ │ ├── config/
│ │ │ ├── lib/
│ │ │ ├── routes/
│ │ │ └── services/
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── frontend/
│ ├── public/
│ ├── src/
│ │ ├── components/
│ │ ├── contexts/
│ │ ├── lib/
│ │ └── services/
│ ├── Dockerfile
│ ├── nginx/
│ └── package.json
├── docs/
│ └── n8n-final-workflow-with-tools.svg
├── packages/
├── scripts/
├── supabase/
├── package.json
└── README.md
- React 19
- TypeScript
- Vite
- CSS Modules
- Supabase JS
- Node.js
- Fastify
- TypeScript
- Supabase JS
- Supabase self-hosted
- n8n self-hosted
- Docker
- Coolify
- Traefik
- Node.js 22+
- npm 10+
- instância Supabase acessível
- backend e frontend configurados com
.env
npm installnpm run dev:frontendFrontend local:
http://localhost:5173
npm run dev:backendBackend local:
http://localhost:3000
npm run build:frontend
npm run build:backendcd apps/frontend
npm run dev
npm run build
npm run preview
npm run lintcd apps/backend
npm run dev
npm run build
npm run startArquivo base: apps/frontend/.env.example
Obrigatórias para login e fluxo principal:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYVITE_API_URL
Observações:
- quando
VITE_API_URLestá presente, o frontend usa o fluxo assíncrono com backend + SSE - sem
VITE_API_URL, o frontend pode cair no fluxo legado
Arquivo base: apps/backend/.env.example
Obrigatórias para o fluxo atual:
PORTALLOWED_ORIGINSUPABASE_URLSUPABASE_ANON_KEYN8N_CHAT_WEBHOOK_URLN8N_CHAT_CALLBACK_SECRETN8N_CHAT_CHANNELN8N_CHAT_SOURCE
Obrigatória para persistência oficial do histórico:
SUPABASE_SERVICE_ROLE_KEY
Opcional:
CHAT_BUFFER_WINDOW_MS
O backend envia ao webhook do n8n um payload limpo com:
{
"message": "texto atual do cliente",
"sessionId": "uuid",
"channel": "web",
"source": "concierge-web",
"userInfo": {
"id": "uuid",
"email": "user@example.com",
"fullName": "Nome do usuário",
"preferredLanguage": "pt-BR",
"originCity": "cidade",
"interests": ["praia", "gastronomia"],
"travelStyle": ["casal"],
"budgetProfile": "luxo",
"companionsSummary": "casal",
"notes": null,
"updatedAt": "2026-04-03T11:50:03.066265+00:00"
}
}O backend espera:
{
"sessionId": "uuid",
"status": "processing | reply | error",
"reply": "texto final da assistente",
"messageId": "uuid-opcional-mas-recomendado"
}Fluxo principal recomendado:
WebhookPayloadCallback ProcessingPrompt BuilderBasic LLM ChainouAI AgentReply PayloadCallBackReplyRespond to Webhook
Se houver tools:
- preferir
AI Agent - usar
Call n8n Workflow Toolpara integrações externas
Diagrama de referência:
docs/n8n-final-workflow-with-tools.svg
O histórico oficial da conversa fica no Supabase:
chat_sessionschat_messages
- mensagem do usuário em
POST /api/chat/messages - resposta da assistente em
POST /api/chat/callback
Mensagens de onboarding local e saudações efêmeras do frontend permanecem fora do histórico oficial por decisão de produto. O contexto definitivo do usuário deve ficar em:
auth.usersuser_profileschat_sessionschat_messages
A UI usa duas camadas:
- cache local por
sessionId - reidratação remota por
GET /api/chat/history/:sessionId
Isso melhora:
- refresh de página
- retomada de conversa
- resiliência contra latência momentânea do histórico remoto
Topologia atual recomendada:
GitHub
|
v
Coolify
|
+-- Frontend
+-- Backend
+-- n8n
+-- Supabase
- frontend web
- backend Fastify
- n8n principal
- n8n worker / webhook, quando aplicável
- Supabase
Backend:
GET /healthz
- o backend precisa do mesmo
ALLOWED_ORIGINdo frontend publicado SUPABASE_SERVICE_ROLE_KEYdeve ficar apenas no backendN8N_CHAT_CALLBACK_SECRETdeve ser conhecido só por backend e n8n- se houver troca de usuário na mesma máquina, o frontend agora isola a sessão por identidade
- o backend faz recuperação defensiva para
chat-session-owner-mismatch, gerando nova sessão quando necessário
docs/n8n-final-workflow-with-tools.svgdocs/README.mdpackages/README.mdscripts/README.md
Este README descreve o estado atual do sistema orientado ao fluxo:
- frontend autenticado
- backend como camada de confiança
- n8n como automação e IA
- Supabase como identidade, perfil e histórico persistido
Se o fluxo de IA ou de tools mudar, atualize primeiro:
- contrato do backend
- payload do n8n
- variáveis de ambiente
- esta documentação
Digital concierge platform focused on travel experiences in the Piauí coastline, with a React frontend authenticated through Supabase, a Fastify backend for secure orchestration, and n8n as the automation and AI runtime.
- Overview
- Current architecture
- Chat flow
- Repository structure
- Stack
- Local environment
- Environment variables
- Backend and n8n contract
- Chat history persistence
- Deployment and operations
- Additional documentation
The repository is organized around clear responsibility boundaries:
apps/frontend: customer-facing web applicationapps/backend: authenticated API for chat, SSE, history, and n8n integrationsupabase/: Supabase-related assetsdocs/: architecture notes and operational diagrams
The current platform priorities are:
- authentication and user context handled in frontend and backend
- n8n as the automation and AI layer, without exposing secrets to the client
- official chat history persisted in Supabase
- resilient chat UX with local cache plus remote rehydration
React Frontend
|
| POST /api/chat/messages
| GET /api/chat/events/:sessionId (SSE)
| GET /api/chat/history/:sessionId
v
Fastify Backend
|
| validates auth with Supabase
| loads user_profiles
| persists chat_sessions/chat_messages
| calls n8n webhook
v
n8n
|
| processes the message
| uses Prompt Builder / Agent / Tools
| sends async callback
v
Fastify Backend
|
| POST /api/chat/callback
| publishes SSE to the client
| persists assistant reply
v
React Frontend
- user authentication through Supabase
- chat UI
- local session and transcript cache
- SSE subscription
- persisted history recovery
- bearer token validation
userInfocomposition fromauth.users+user_profiles- per-user session creation and isolation
- async orchestration with n8n
- official history persistence in Supabase
- chat automation
- prompt composition
- model execution
- future tool usage, such as Google Places
- callback to the backend with
processing,reply, anderror
- the authenticated frontend sends
messageandsessionIdtoPOST /api/chat/messages - the backend validates the token and builds
userInfo - the backend registers the session in the event bus and persists the user message
- the backend dispatches the n8n webhook
- the backend immediately returns
202 Accepted - the frontend opens SSE on
GET /api/chat/events/:sessionId - n8n processes the request and calls
POST /api/chat/callback - the backend publishes
buffering,processing,reply, orerror - the frontend updates the UI in real time
GET /healthzPOST /api/chat- legacy synchronous route
POST /api/chat/messages- main asynchronous route
GET /api/chat/events/:sessionId- SSE for chat delivery
GET /api/chat/history/:sessionId- persisted chat history
POST /api/chat/callback- authenticated n8n callback
concicerge/
├── apps/
│ ├── backend/
│ │ ├── src/
│ │ │ ├── config/
│ │ │ ├── lib/
│ │ │ ├── routes/
│ │ │ └── services/
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── frontend/
│ ├── public/
│ ├── src/
│ │ ├── components/
│ │ ├── contexts/
│ │ ├── lib/
│ │ └── services/
│ ├── Dockerfile
│ ├── nginx/
│ └── package.json
├── docs/
│ └── n8n-final-workflow-with-tools.svg
├── packages/
├── scripts/
├── supabase/
├── package.json
└── README.md
- React 19
- TypeScript
- Vite
- CSS Modules
- Supabase JS
- Node.js
- Fastify
- TypeScript
- Supabase JS
- self-hosted Supabase
- self-hosted n8n
- Docker
- Coolify
- Traefik
- Node.js 22+
- npm 10+
- accessible Supabase instance
- backend and frontend configured with
.env
npm installnpm run dev:frontendLocal frontend:
http://localhost:5173
npm run dev:backendLocal backend:
http://localhost:3000
npm run build:frontend
npm run build:backendcd apps/frontend
npm run dev
npm run build
npm run preview
npm run lintcd apps/backend
npm run dev
npm run build
npm run startBase file: apps/frontend/.env.example
Required for login and the main chat flow:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYVITE_API_URL
Notes:
- when
VITE_API_URLis present, the frontend uses the asynchronous backend + SSE flow - without
VITE_API_URL, the frontend may fall back to the legacy path
Base file: apps/backend/.env.example
Required for the current flow:
PORTALLOWED_ORIGINSUPABASE_URLSUPABASE_ANON_KEYN8N_CHAT_WEBHOOK_URLN8N_CHAT_CALLBACK_SECRETN8N_CHAT_CHANNELN8N_CHAT_SOURCE
Required for official history persistence:
SUPABASE_SERVICE_ROLE_KEY
Optional:
CHAT_BUFFER_WINDOW_MS
The backend sends a normalized payload to the n8n webhook:
{
"message": "latest user message",
"sessionId": "uuid",
"channel": "web",
"source": "concierge-web",
"userInfo": {
"id": "uuid",
"email": "user@example.com",
"fullName": "User Name",
"preferredLanguage": "pt-BR",
"originCity": "city",
"interests": ["praia", "gastronomia"],
"travelStyle": ["casal"],
"budgetProfile": "luxo",
"companionsSummary": "casal",
"notes": null,
"updatedAt": "2026-04-03T11:50:03.066265+00:00"
}
}The backend expects:
{
"sessionId": "uuid",
"status": "processing | reply | error",
"reply": "final assistant text",
"messageId": "uuid-optional-but-recommended"
}Recommended main flow:
WebhookPayloadCallback ProcessingPrompt BuilderBasic LLM ChainorAI AgentReply PayloadCallBackReplyRespond to Webhook
If tools are introduced:
- prefer
AI Agent - use
Call n8n Workflow Toolfor external integrations
Reference diagram:
docs/n8n-final-workflow-with-tools.svg
Official conversation history lives in Supabase:
chat_sessionschat_messages
- user messages in
POST /api/chat/messages - assistant replies in
POST /api/chat/callback
Local onboarding prompts and ephemeral frontend greetings stay outside the official history by product decision. The definitive user context should live in:
auth.usersuser_profileschat_sessionschat_messages
The UI uses two layers:
- local cache keyed by
sessionId - remote rehydration via
GET /api/chat/history/:sessionId
This improves:
- page refresh recovery
- conversation resumption
- resilience against temporary latency in remote history
Recommended production topology:
GitHub
|
v
Coolify
|
+-- Frontend
+-- Backend
+-- n8n
+-- Supabase
- frontend web app
- Fastify backend
- main n8n instance
- n8n worker / webhook, when applicable
- Supabase
Backend:
GET /healthz
- the backend must allow the same
ALLOWED_ORIGINused by the published frontend SUPABASE_SERVICE_ROLE_KEYmust stay backend-onlyN8N_CHAT_CALLBACK_SECRETmust be shared only between backend and n8n- when users switch accounts on the same machine, the frontend now isolates chat sessions by identity
- the backend performs defensive recovery for
chat-session-owner-mismatchby issuing a new session when needed
docs/n8n-final-workflow-with-tools.svgdocs/README.mdpackages/README.mdscripts/README.md
This README documents the current system as it actually operates:
- authenticated frontend
- backend as the trust boundary
- n8n as the automation and AI layer
- Supabase as identity, profile, and persisted chat history
If the AI flow or tool layer changes, update these in order:
- backend contract
- n8n payload shape
- environment variables
- this documentation