Documentação Técnica: Multi-Tenant Headless Blogs & LinkPages
Esta documentação descreve a arquitetura, modelagem de dados, suporte a internacionalização (i18n), recursos de SEO e especificações da API Headless para os novos módulos de Blogs e LinkPages (páginas de links estilo Linktree).
ambos os módulos foram projetados para permitir que uma única Community (tenant) gerencie múltiplas instâncias (vários blogs e várias páginas de links), cada uma podendo ser mapeada para um domínio customizado próprio (ex: blog.acme.com ou bio.acme.com).
🏗️ 1. Visão Geral da Arquitetura Multi-Tenant & Multi-Domain
┌──────────────────────────┐
│ Community (Tenant) │
└────────────┬─────────────┘
│
┌──────────────────────┴──────────────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ 1..N Blogs │ │ 1..N LinkPages │
│ (apps/blogs) │ │ (apps/link_pages) │
└──────────┬──────────┘ └──────────┬──────────┘
│ │
Domain: blog.com │ Domain: dev.acme.io Domain: bio.com │ Domain: links.acme.io
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Headless API Endpts │ │ Headless API Endpts │
└─────────────────────┘ └─────────────────────┘Roteamento por Domínio Customizado
O backend Django resolve automaticamente qual Blog ou LinkPage deve responder através das seguintes opções (em ordem de precedência):
- Query string:
?domain=blog.suamarca.com - HTTP Header:
X-Blog-Domain: blog.suamarca.comouX-Tenant-Domain: bio.suamarca.com - Host Header nativo da requisição HTTP:
Host: blog.suamarca.com
📚 2. Módulo de Blogs (apps/blogs)
Modelos de Dados
Blog:community:ForeignKey(Community)(Dono do Blog).name: Nome do blog.slug: Identificador dentro da comunidade.domain: Domínio/subdomínio customizado (único no sistema).default_language: Idioma padrão (ex:pt-BR).available_languages: Idiomas suportados em JSON (ex:["pt-BR", "en", "es"]).i18n: Dicionário com traduções (name,description).settings: Configurações de tema, branding e scripts de analytics.
BlogPost:blog:ForeignKey(Blog).title,slug,excerpt.content_html: HTML sanitizado (via allow-list do TipTap/nh3).content_json: AST/JSON estruturado do TipTap para renderização em frontends React/Next.js/Astro.status:draft,published,archived.published_at: Data e hora de publicação.seo_metadata: Overrides de SEO (meta_title,meta_description,og_image,keywords).i18n: Traduções multilíngue.
BlogCategory&BlogTag: Taxonomias do blog.
Endpoints da API Headless (/api/v1/blogs/ ou /api/headless/v1/blogs/)
| Método | Endpoint | Descrição |
|---|---|---|
GET | /resolve/?domain=blog.acme.com | Resolve e retorna as configurações do blog pelo domínio. |
GET | /<blog_slug>/ | Detalhes e informações públicas do blog. |
GET | /<blog_slug>/posts/ | Lista posts publicados (suporta ?category=, ?tag=, ?search=, ?lang=). |
GET | /<blog_slug>/posts/<post_slug>/ | Detalhes de um post publicado com SEO completo e i18n. |
GET | /<blog_slug>/categories/ | Lista todas as categorias ativas do blog. |
GET | /<blog_slug>/tags/ | Lista todas as tags do blog. |
GET | /<blog_slug>/sitemap/ | Retorna a estrutura de Sitemap para motores de busca. |
🔗 3. Módulo de Páginas de Links (apps/link_pages)
Modelos de Dados
LinkPage:community:ForeignKey(Community).title: Título principal / Nome do perfil.slug: Slug identificador.domain: Domínio customizado (único).bio: Descrição ou resumo do perfil.avatar&favicon: Arquivos de imagem.theme_config: JSON com opções de layout (cores de botão, fundos gradientes, fontes).i18n: Dicionário multilíngue.
LinkItem:page:ForeignKey(LinkPage).item_type:link: Botão de link padrão (com suporte a miniatura, ícone e badgebadge_text).header: Cabeçalho de seção de texto.social_row: Barra horizontal com ícones de redes sociais.embed: Mídia incorporada (YouTube, Spotify, Soundcloud).newsletter: Formulário de captura de e-mails.
title,subtitle,url,icon,thumbnail,badge_text.order: Ordem de exibição.is_highlighted: Destaque visual/animação no botão.i18n: Traduções multilíngue.
Endpoints da API Headless (/api/v1/link-pages/ ou /api/headless/v1/link-pages/)
| Método | Endpoint | Descrição |
|---|---|---|
GET | /resolve/?domain=bio.acme.com | Resolve a página de links completa pelo domínio. |
GET | /<page_slug>/ | Detalhes da página, lista de botões/itens ativos e dados de SEO. |
GET | /<page_slug>/sitemap/ | Sitemap dinâmico da página de links. |
🌐 4. Suporte a Internacionalização (i18n)
Ambos os módulos estendem a classe abstrata TranslatableModel do projeto:
- Campos Dinâmicos Traduzíveis: Guardados na propriedade
i18n(JSONField):json{ "en": { "title": "Django Development", "excerpt": "Learn Django fast", "content_html": "<p>English content...</p>" }, "es": { "title": "Desarrollo en Django", "excerpt": "Aprende Django rápido" } } - Negociação de Idioma na API: A API detecta o idioma preferido na seguinte ordem:
- Parâmetro de Query URL
?lang=en - HTTP Header
Accept-Language: en-US,en;q=0.9 - Idioma Padrão configurado no
Blog/LinkPage(default_language)
- Parâmetro de Query URL
⚡ 5. Recursos Avançados de SEO
Cada endpoint de detalhe do BlogPost e LinkPage entrega um objeto seo_metadata completo pronto para ser renderizado pelo frontend:
- Schema.org Rich Snippets (JSON-LD):
- Blogs entregam a especificação
BlogPosting. - Páginas de links entregam a especificação
ProfilePage/WebPage.
- Blogs entregam a especificação
- OpenGraph & Twitter Cards:
title,description,imageetypedevidamente configurados. - Dynamic Alternate Links (hreflang): Lista de links alternativos com seus respectivos idiomas configurados para prevenir conteúdo duplicado no Google.
- Canonical URLs: Geração da URL canônica baseada no domínio configurado no backend.
- Endpoints de Sitemap: Endpoints dedicated
/sitemap/em formato estruturado.