Flaubert CMS — Content Manager AI

Plataforma SaaS que gera conteúdo com IA, faz auditoria SEO/GEO/AEO, gerencia imagens e publica em sites WordPress remotamente via REST API — sem instalar plugin obrigatório no site gerenciado. Arquitetura em monorepo: frontend React (SPA) desacoplado de um backend PHP 8.2 que expõe a API e serve o build do front.

PHP 8.2 React 19 + TanStack MySQL / MariaDB JWT Fila de jobs cms.flaubert.dev.br

Documento gerado a partir da leitura do código-fonte atual. Descreve o comportamento em produção — não altera nada.

1 Visão geral

Duas aplicações independentes no mesmo repositório, comunicando por HTTP através de um proxy.

frontend/

SPA em React 19 (Vite + TanStack Router/Start), UI shadcn/Radix + Tailwind v4. Gerado/versionado via Lovable. Roda local com Bun (bun run dev) e vai para produção como build estático.

backend/

API e CMS em PHP 8.2 (/api/v1/*). ORM próprio, JWT, fila assíncrona, integrações com IA, WordPress, Google e Alexa. Hospedado em cPanel/LiteSpeed.

Como front e back conversam

O PHP não envia cabeçalhos CORS, então o navegador não chama a API diretamente. O front expõe uma rota-proxy server-side (src/routes/api/proxy/$.ts) que roda no mesmo domínio e repassa tudo para o PHP:

Navegador /api/proxy/* Worker (proxy) cms.flaubert.dev.br/api/v1/* PHP

O cliente HTTP central do front (src/lib/api.ts) sempre aponta para /api/proxy, anexa o JWT do localStorage e trata refresh automático em 401. Cabeçalhos de proxy/CDN (X-Forwarded-For, CF-*) são removidos no repasse.

Produção: em cms.flaubert.dev.br o próprio .htaccess do backend serve o index.html do SPA para qualquer rota que não seja arquivo real, pasta real, /api/ ou /aprovacao/. Ou seja, o mesmo domínio entrega o app React e a API.

2 Stack tecnológica

CamadaTecnologia
Frontend frameworkReact 19, TanStack Router + TanStack Start, TanStack Query
Build / runtimeVite 8, Bun, Nitro (Worker/Cloudflare), TypeScript 5.8
UIshadcn/ui sobre Radix UI, Tailwind CSS v4, lucide-react, recharts, sonner, react-hook-form + zod
BackendPHP 8.2 (LiteSpeed/lsphp), ORM próprio (PDO), sem framework externo
BancoMySQL / MariaDB (utf8mb4), schema em backend/database/
AutenticaçãoJWT próprio (access + refresh), suporte a 2FA (TOTP), blacklist de tokens
AssíncronoFila de jobs em tabela jobs, worker via cron.php (CLI) ou inline
IA (texto)Claude (Anthropic), Gemini (Google), OpenAI
ImagensOpenAI gpt-image-1 (IA), Pexels e Unsplash (stock), upload manual, processamento com GD
IntegraçõesWordPress REST API + Application Passwords, Google Search Console/Indexing API, Alexa Proactive Events, e-mail (SMTP), WhatsApp
MCPServidor Node/Express expondo ferramentas ao Claude Desktop

3 Estrutura de pastas

Projeto CMS/
├── README.md                     Monorepo (instruções de deploy)
├── Plano-de-Acao-SaaS.md         Plano de virar SaaS multi-tenant
├── Alexa-Integracao-Guia.md
├── Google-Search-Console-Guia.md
│
├── frontend/                     SPA React (Vite + TanStack)
│   ├── src/
│   │   ├── routes/               1 arquivo = 1 tela (dashboard, editor, sites…)
│   │   │   └── api/proxy/$.ts    Proxy server-side → PHP
│   │   ├── components/           AdminShell, AppSidebar, Topbar, AuthGuard…
│   │   │   └── ui/               46 componentes shadcn/ui
│   │   ├── lib/                  api.ts, auth.ts, api-types.ts, utils.ts
│   │   └── hooks/
│   ├── package.json  vite.config.ts  .env (API_BASE_URL)
│
└── backend/                      API + CMS em PHP 8.2
    ├── .htaccess                 Roteamento (SPA + API + /aprovacao)
    ├── .env.example              Modelo de configuração
    ├── cron.php                  Worker da fila (CLI) + limpezas
    ├── api/v1/                   19 endpoints REST
    ├── core/
    │   ├── bootstrap.php         Carrega .env, autoload, handlers de erro
    │   ├── Database.php  ContentService.php
    │   ├── auth/                 AuthManager, RoleManager
    │   ├── middleware/           Auth, Permission, MiddlewareStack
    │   ├── models/               ORM (Model, QueryBuilder) + 20 models
    │   ├── providers/adapters/   Claude, Gemini, OpenAI, Pexels, Unsplash
    │   ├── queue/                Queue, Job + 5 jobs
    │   └── services/             ~18 serviços (regra de negócio)
    ├── database/                 schema.sql + migrations
    ├── admin/                    Sobras (dicas-conteudo.html, esta doc, css/js)
    ├── mcp-server/               Servidor MCP (Node/Express)
    ├── wordpress-mu-plugin/      cms-faq-rest-bridge.php (instalar no WP)
    ├── install/                  Instalador web (wizard)
    └── storage/  uploads/        Logs, cache, mídia temporária
Legado: as telas antigas em Alpine.js (editor.html, historico.html etc.) foram migradas para o SPA React. Em backend/admin/ restam apenas arquivos utilitários (dicas-conteudo.html, footer.js, sidebar.js, shell.css) — e agora este documentacao.html.

4 Núcleo do backend (bootstrap e ORM)

bootstrap.php

Ponto de entrada de todo endpoint (require_once core/bootstrap.php). Responsável por:

  • Sessão: só inicia se ainda não houver sessão ativa (evita warning em inclusão aninhada).
  • Parser de .env manual: não usa parse_ini_file() (falhava com #, ;, =, aspas em senhas/chaves). Faz parse linha a linha e remove aspas envolventes.
  • Controle de erros: display_errors fica OFF por padrão (um warning no corpo quebra o JSON do front). Erros vão para storage/logs/php-errors.log. APP_DEBUG=1 liga a exibição só em dev.
  • Handlers globais: set_exception_handler devolve JSON de erro genérico em vez de resposta vazia.
  • Autoload de classes do core/ e carga explícita do Validator.php (funções globais soltas não disparam o autoload).

ORM próprio

Sem Eloquent/Doctrine. core/models/Model.php + QueryBuilder.php implementam um mini-ORM sobre PDO:

  • fill() respeita $fillable (proteção contra mass-assignment na entrada de usuário).
  • hydrate() reconstrói objetos a partir de linhas reais do banco aceitando todas as colunas (inclusive id, created_at) — sem abrir brecha via API.
  • Dirty tracking: update() só grava os campos realmente alterados.
  • Helpers where(), whereNull(), whereNotNull(), first(), get().
  • Database.php normaliza booleanos para 1/0 (com EMULATE_PREPARES=false, true/false viravam string vazia).

Roteamento

Cada arquivo em api/v1/ é um roteador simples: lê REQUEST_METHOD + REQUEST_URI e casa a rota com preg_match/strpos, respondendo com json_response(). Autenticação via getAuthenticatedUser(); erros centralizados em handleApiException() (loga o real, devolve mensagem genérica).

5 Autenticação e papéis

JWT próprio com access token + refresh token, 2FA opcional (TOTP) e blacklist de tokens revogados.

  • Login devolve token + refresh_token; o front guarda no localStorage e renova em 401 via auth/refresh.
  • Logout adiciona o token à token_blacklist; o cron.php limpa tokens expirados diariamente.
  • Recuperação de senha: auth/password/forgot e auth/password/reset.

Papéis (RoleManager)

PapelPermissões
admin* — acesso total, incl. configurações, usuários, chaves de API, limpeza de histórico.
gerente* — igual ao admin na prática (evita 403 em criar/editar/publicar). Distinção histórica: não limpa histórico.
editorposts.create, posts.edit, posts.publish, posts.delete, sites.view, sites.create
redatorposts.create, posts.edit (não publica nem exclui)

Verificação por RoleManager::can($role, $permission). Endpoints sensíveis (usuários, publicação) também revalidam papel. Recursos são checados por dono (helpers findContentForUser / findSiteForUser) para evitar IDOR.

6 Endpoints da API (/api/v1)

19 arquivos de rota. Abaixo, o mapa completo por recurso.

Autenticação — auth.php

POST/auth/loginLogin (e-mail + senha, 2FA se ativo)
POST/auth/registerRegistro
POST/auth/refreshRenova access token
POST/auth/logoutRevoga token (blacklist)
POST/auth/password/forgot · /resetRecuperação de senha
POST GET/auth/usersAdmin cria / lista usuários (qualquer papel)
PUT DEL/auth/users/{id}Edita / remove usuário

Posts / conteúdo — posts.php

GET/postsLista (filtro ?status=, exclui soft-deleted)
POST/postsCria conteúdo
GET PUT DEL/posts/{id}Lê / atualiza / exclui (soft delete)
GET/posts/{id}/versionsVersões do post
POST/posts/{id}/revert/{version}Reverte para versão
POST/posts/{id}/restoreRestaura da lixeira
POST/posts/{id}/publish · /schedulePublica / agenda
POST/posts/{id}/request-approvalEnvia para aprovação do cliente
POST/posts/check-grammarCorreção ortográfica (LanguageTool)
POST/posts/generate-faqGera FAQ (perguntas/respostas) via IA
POST/posts/generate-seo-title · /generate-meta-descriptionGera título SEO / meta description via IA
GET/posts/history · /history/yearsHistórico de publicações e anos p/ filtro

Sites — sites.php

GET POST/sitesLista / cria site
GET PUT DEL/sites/{id}Lê / atualiza / remove
POST/sites/test-connectionTesta credenciais WordPress
GET/sites/{id}/categories · /authorsCategorias e autores do WP (cache 1h)
GET/sites/{id}/faq-capability · /approval-capabilityDetecta recursos do site

Publicação, mídia e fila

POST/publish/now · /publish/schedulePublica agora / agenda
GET/media?content_id=Imagem destacada do post
POST/media/uploadUpload manual de imagem
PUT DEL/media/{id}Atualiza alt/title/caption (local + WP) / exclui
POST GET/queue/jobs · /queue/jobs/{id}Enfileira / consulta status de job

IA, imagens e limites

POST/ai-images (via job)Gera/processa imagem para conteúdo
GET/ai-images/can-generateVerifica se ainda há cota
GET/ai-images/limits/dashboard · /dashboard-countdownPainel de limites + contagem regressiva de reset
POST PUT/ai-images/limits · /limits/{id}Cria / edita cota de imagens
POST/ai-images/limits/{id}/resetReset manual da cota
POST/ai-images/alerts/{id}/acknowledgeMarca alerta como lido
GET/tokens/summary · /dashboard · /usage · /checkConsumo/custo de tokens de IA
GET PUT/image-settings?site_id=Config de imagem por site
POST/ai-test · /alexa-testTesta conexão IA / envio Alexa

Configurações, relatórios e observabilidade

GET PUT/settings/ai-defaultsProvider padrão de texto/imagem
GET PUT/settings/api-keysStatus mascarado / atualização de chaves
GET PUT/settings/notificationsSMTP / WhatsApp
GET PUT DEL/settings/themeCores personalizadas (claro/escuro)
POST/settings/test-emailEnvia e-mail de teste (SMTP salvo)
GET/reportRelatório mensal
GET/historyHistórico/eventos
GET POST/backupExporta / importa backup
GET POST DEL/branding · /branding/{logo|favicon}Logo / favicon / marca
GET PATCH POST/logs · /logs/channels · /logs/{id} · /logs/clear-allLog central (resolver/limpar entradas)

Google Search Console — google.php

GET/google/connect?site_id= · /callbackFluxo OAuth do cliente
GET/google/status?site_id=Estado da conexão
POST/google/test · /disconnectValida credenciais / desconecta
POST/google/detect-sitemap · /reindexDetecta sitemap / pede reindexação
PUT/google/sitemapDefine o sitemap
GET/google/performance?content_id=&days=Desempenho da URL no GSC

Aprovação pública — approval-public.php

GET/aprovacao/{token}Página de confirmação (sem login, renderizada em PHP)
POST/aprovacao/{token}Cliente aprova / pede ajustes / rejeita

7 Serviços (regra de negócio)

A lógica vive em core/services/; os endpoints são finos e delegam para cá.

ServiçoResponsabilidade
ContentServiceCRUD de conteúdo, versionamento, geração de alternativa por IA, histórico, requisitos mínimos, status de aprovação, parse de FAQ da IA, validação SEO.
PublishServicePublica/agenda no WordPress; publishDue() publica agendados vencidos (chamado pelo cron), disparando o gancho de indexação.
SiteManagerCria sites, testa conexão WP, busca categorias/autores (cache), detecta capacidade de FAQ.
SEOServiceAuditoria estilo Yoast: título, meta description, densidade de palavra-chave (normaliza acentos), H1/H2, regras por canal.
ImageServiceProcessa imagem com GD (redimensiona, libera memória no finally), envia ao WordPress, atualiza metadados da mídia no WP.
ImageGenerationServiceOrquestra geração de imagem para um conteúdo (provider + limites + salvamento).
ApprovalServiceCanais disponíveis, solicita aprovação, processa resposta do cliente, invalida pendências, notifica expiração.
NotificationServiceEnvio de e-mail (SMTP ou mail() nativo) e WhatsApp.
AlexaNotificationServiceEventos proativos na Alexa (aprovado/ajustes/rejeitado) via API oficial.
GoogleSearchConsoleServiceOAuth por site, reenvio de sitemap, pedido de indexação, desempenho de URL, detecção de sitemap.
TokenServiceEstima custo, registra consumo, resumo (hoje/mês/total), verifica disponibilidade, consome cota.
QuotaManager / AiImageLimiterCotas de imagens IA (diária/semanal/mensal/custom), uso, alertas, reset com avanço de janela, histórico.
SettingsServiceOrdem de fallback de providers de texto, provider de imagem, chaves de API (mascaradas), notificações, tema.
RateLimiterLimite por identificador/janela; IP só usa cabeçalho de proxy se TRUSTED_PROXY=1.
ValidatorValidação/sanitização de entrada (e-mail, URL, HTML).

8 Provedores de IA e imagem

Padrão adapter em core/providers/adapters/, orquestrados por ProviderManager com ordem de fallback configurável.

ClaudeProvider

Anthropic — geração de texto.

GeminiProvider

Google — texto.

OpenAIProvider

Texto + imagem (gpt-image-1, retorna base64).

PexelsProvider

Imagens de banco (stock).

UnsplashProvider

Imagens de banco (stock).

Todos verificam erro de conexão cURL explicitamente (evita o confuso "HTTP 0"). Consumo é registrado em token_usage / ai_image_usage com custo estimado. O provider padrão de texto e de imagem é definido em /settings/ai-defaults.

Ciclo de vida de modelos: modelos podem ser aposentados sem aviso (o DALL·E 3 foi trocado por gpt-image-1, que devolve base64 em vez de URL — exigindo salvar arquivo temporário local antes do upload).

9 Fila de jobs e cron

Trabalho pesado (gerar conteúdo, imagem, publicar, validar SEO) roda de forma assíncrona na tabela jobs.

Jobs disponíveis

GenerateContentJob

Gera o texto do post via IA.

GenerateImageJob

Gera/processa a imagem.

GenerateFaqJob

Gera FAQ via IA.

PublishJob

Publica no WordPress.

SeoValidationJob

Roda auditoria SEO.

Worker (cron.php)

  • Só roda via CLI (bloqueia acesso por navegador). Configurar * * * * * a cada minuto.
  • Processa até 10 jobs por execução; lock atômico (UPDATE ... WHERE status='pending') evita processamento duplicado.
  • Recupera jobs travados em "processing" por 3+ minutos (recoverStalled()).
  • Publica agendados vencidos via PublishServicemas se há aprovação pendente, marca o post como expired e avisa o redator (segurança: não publica sem aprovação).
  • Limpezas periódicas: remove tokens expirados da blacklist.
  • Ambientes sem cron: existe um worker inline como fallback.

10 Modelo de dados

MySQL/MariaDB, utf8mb4. Schema completo em backend/database/schema.sql; alterações incrementais em migrate_*.sql.

TabelaDescrição
usersUsuários. Papel (admin/gerente/editor/redator), plano (basic/pro/agency), 2FA.
sitesSites WordPress gerenciados (URL, credenciais WP, e-mail/WhatsApp do cliente, sitemap, aprovação padrão).
site_credentialsCredenciais extras por site (FTP, cPanel etc.).
contentsConteúdo/posts: título, seo_title, corpo, meta, focus_keyword, categorias/autor WP, faq_data, status, agendamento, soft delete, flags de aprovação do cliente.
content_approvalsEnvios de aprovação: token UUID único, status, canal, IP de resposta, comentário.
content_versionsHistórico de versões (título/corpo) por post.
content_eventsLinha do tempo (criado, aprovação, publicado, falha…). Usada pelo Histórico.
mediaImagens: URL, wp_media_id, alt/title/caption/description, origem, slug.
token_usage / token_quotasConsumo e cotas de tokens de IA (texto) por usuário/provider/modelo.
ai_generated_imagesRegistro de cada imagem gerada por IA (prompt, custo, tempo).
ai_image_quotas / _usage / _alertsSistema de limites de imagem IA: cotas (diária/semanal/mensal/custom), uso, alertas.
quota_reset_historyAuditoria de resets de cota.
image_settingsConfig de imagem por (usuário, site): fonte, provider, dimensões, templates de prompt.
jobsFila assíncrona (tipo, payload, status, tentativas, resultado).
seo_validation_resultsResultados de auditoria SEO (score, status, issues, sugestões).
audit_logsAuditoria de ações (quem, o quê, IP, resultado).
system_logsLog técnico central (warning/error/critical) por canal (IA, WP, fila, GSC…).
token_blacklistTokens JWT revogados (logout), limpos pelo cron.
site_google_connectionsOAuth do Google do cliente por site (access/refresh token, propriedade GSC).

11 Integração WordPress

Publicação remota via REST API + Application Passwords. O plugin no site é opcional (só necessário para FAQ Schema e para contornar bloqueios de segurança).

  • Publicação: PublishService cria/atualiza o post via /wp-json/wp/v2/posts, enviando categorias, autor e a imagem destacada.
  • Categorias/autores: buscados via REST com cache de 1h, reutilizando as credenciais já salvas do site.
  • Mídia: imagem enviada para a biblioteca do WP; metadados (alt/title/caption) sincronizados.

mu-plugin: cms-faq-rest-bridge.php

Instalar em wp-content/mu-plugins/ no site gerenciado. Faz duas coisas, de forma puramente aditiva (não edita plugins existentes):

  • Expõe as 9 metas _mseo_faq_* do plugin "FAQ Schema" na REST API (com auth_callback exigindo edit_post e a mesma sanitização do plugin), gerando FAQPage Schema.org.
  • Cria a rota /wp-json/cms-faq/v1/authors, contornando bloqueios do Wordfence/iThemes Security ao endpoint padrão /wp/v2/users. Também expõe /status para a plataforma detectar o plugin.

12 Fluxo de aprovação do cliente

O cliente aprova o conteúdo por um link sem login, recebido por e-mail ou WhatsApp.

Redator solicita aprovação Link com token UUID /aprovacao/{token} (PHP) Aprova / Ajustes / Rejeita Notifica equipe (+ Alexa)
  • A rota /aprovacao/ é PHP renderizado no servidor, fora do SPA (o .htaccess a exclui do React) — por isso funciona sem login.
  • Cada envio gera um token único; envios antigos ficam superseded. O IP e o comentário do cliente são registrados para auditoria.
  • Se um post agendado tem aprovação pendente na hora de publicar, o cron o marca expired e avisa o redator — nunca publica sem aprovação.
  • Canais dependem do que o site tem cadastrado (e-mail e/ou WhatsApp).

13 Google Search Console

Ao publicar/aprovar, o CMS avisa o Google. Conexão OAuth é por site (o cliente é o dono da propriedade).

  • Reenvio de sitemap via Search Console API — funciona e ajuda o Google a descobrir URLs novas mais rápido.
  • Pedido de indexação via Indexing API — a API é oficialmente só para JobPosting/BroadcastEvent; para blog comum o Google ignora. Conte com o reenvio de sitemap como ganho real.
  • Botão manual "Reindexar no Google" e consulta de desempenho da URL (/google/performance).
  • Credenciais OAuth criadas uma vez para a plataforma; cada cliente conecta a própria conta. Detecção automática de sitemap disponível.

Detalhes de configuração em Google-Search-Console-Guia.md.

14 Integração Alexa

Notificações proativas na Alexa usando a API oficial de Proactive Events da Amazon — sem AWS Lambda, tudo roda no servidor.

  • Eventos cobertos: cliente aprovou, pediu ajustes, rejeitou uma publicação.
  • Serviço dedicado AlexaNotificationService (isConfigured(), sendProactiveEvent(), notifyApproval()). Arquitetura modular: novo evento = novo método.
  • Endpoint de teste: POST /api/v1/alexa-test.

Passo a passo em Alexa-Integracao-Guia.md.

15 MCP Server

Servidor Node/Express (backend/mcp-server/) que expõe a plataforma como ferramentas para o Claude Desktop.

  • Configuração por ambiente: PLATFORM_API_URL, PLATFORM_API_KEY, MCP_PORT (padrão 3000).
  • Ferramentas expostas incluem generate-content (gera post por site/tópico/keyword, com opção de auto-publicar) e search-and-process-image, entre outras.
  • Chama a mesma API /api/v1 — é um cliente, não um caminho paralelo.

16 Frontend (SPA React)

TanStack Router com roteamento por arquivo: cada src/routes/*.tsx é uma tela. Layout comum em AdminShell (sidebar + topbar), proteção por AuthGuard.

RotaTela
/ (index)Login (redireciona ao dashboard se já logado)
/dashboardPainel principal (posts, atalhos)
/editorEditor de conteúdo: SEO/GEO/AEO em tempo real, categorias, FAQ, imagem, aprovação
/sitesGerência de sites WordPress
/historicoHistórico de publicações (timeline, filtros, status, quem excluiu)
/imagensConfigurações de imagem por site
/limites-iaLimites e consumo de IA/imagens
/relatoriosRelatório mensal (imprimível)
/logsLog central técnico
/usuariosGestão de usuários e papéis
/configuracoesChaves de IA, notificações, tema, branding, backup
/forgot-passwordRecuperação de senha

Camada de dados

  • lib/api.ts — cliente HTTP único (proxy + JWT + refresh + HttpError).
  • lib/auth.ts — sessão/token no localStorage.
  • lib/api-types.ts — tipos TypeScript espelhando as respostas do PHP.
  • TanStack Query para cache/estado de servidor; react-hook-form + zod nos formulários; sonner para toasts.
Lovable: o projeto é conectado ao Lovable. Commits na branch conectada sincronizam com o editor — evite reescrever histórico já publicado (force-push/rebase/squash).

17 Configuração (.env)

Segredos só no .env do backend (fora do git). Modelo em backend/.env.example.

GrupoChaves
BancoDB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
IA (texto)ANTHROPIC_API_KEY, GOOGLE_API_KEY, OPENAI_API_KEY
Imagens stockUNSPLASH_API_KEY, PEXELS_API_KEY
AppAPP_URL, JWT_SECRET, APP_DEBUG (0 em prod), TRUSTED_PROXY
CacheCACHE_DRIVER (file/redis/memcached) + hosts/portas
NotificaçõesSMTP_*, MAIL_FROM*, MAIL_USE_NATIVE, WHATSAPP_API_URL/TOKEN

No frontend, frontend/.env define API_BASE_URL (destino do proxy; padrão https://cms.flaubert.dev.br).

Nunca deixe APP_DEBUG=1 em produção (vaza detalhes internos) nem comite .env, build (dist/, assets/ gerado) ou node_modules/.

18 Deploy

  1. cd frontend && bun install && bun run build
  2. Enviar backend/ + o build do frontend (index.html + assets/) para a raiz do servidor.
  3. Configurar backend/.env a partir de backend/.env.example.
  4. Banco: manter o existente; aplicar migrations de backend/database/ na ordem indicada.
  5. Cron: agendar php /caminho/cron.php a cada minuto.

Servidor de produção: cPanel + LiteSpeed (lsphp ea-php82) em cms.flaubert.dev.br. O .htaccess cuida do roteamento: arquivo real → arquivo; /api/ → PHP; /aprovacao/ → PHP público; resto → index.html do SPA. Também bloqueia acesso web direto a .env, core/, database/, storage/.

19 Segurança (correções consolidadas)

Endurecimentos aplicados ao longo das versões (ver backend/CHANGELOG.md e ALTERACOES.md).

  • IDOR: posts, mídia e publicação verificam o dono antes de operar.
  • Mass-assignment: PUT não pode reatribuir user_id/site_id; só campos editáveis explícitos.
  • Vazamento de senha WP: wp_password oculto nas respostas da API.
  • Erros 500: tratador central loga o real e devolve mensagem genérica (não expõe MySQL/paths).
  • Rate limiter à prova de spoofing: só confia em cabeçalho de proxy com TRUSTED_PROXY=1.
  • Instalador: bloqueado após instalado (.installed); cron.php e setup.php protegidos.
  • Cabeçalhos: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection; storage/.htaccess bloqueia acesso direto.
  • Fila: lock atômico evita processamento duplicado; recuperação de jobs travados.

20 Roadmap SaaS

Direção definida em Plano-de-Acao-SaaS.md: transformar em SaaS multi-tenant escalável sem afetar clientes atuais.

Os 4 desacoplamentos

  1. Cloudflare na frente de tudo (troca de origem invisível ao cliente).
  2. Mídia em object storage (Cloudflare R2 / Backblaze B2) — a mídia nunca se move.
  3. Banco portátil (dump/import; depois banco gerenciado).
  4. App stateless (JWT + config em .env) — já em grande parte atendido.

Multi-tenant (principal item da Fase 0)

  • Tabela organizations; organization_id em users, sites, contents, media, settings, uso/limites.
  • Toda query filtrada pela organização (escopo global); settings por organização.
  • Papel admin vira admin da organização; acima, um super-admin.
  • BYOK (cada cliente com sua chave de IA) + limites por tenant.

Fases seguintes: cobrança recorrente (Asaas/Mercado Pago/Iugu/Vindi com Pix/boleto), travar recursos por plano, e por fim migração para VPS/infra dedicada quando o volume justificar.

Esta documentação reflete o estado atual do código lido em backend/ e frontend/. Para mudanças de comportamento, atualizar também as fontes citadas (CHANGELOG, ALTERACOES e os guias na raiz).