App de exemplo que edita Markdown com CodeMirror 6, pré-visualiza em HTML e gera PDF via um backend em Go + Pandoc/LaTeX. Inclui cache por conteúdo, PDF de erro com log de compilação e autenticação Bearer por variável de ambiente.
[Editor/Preview (React)] ── ► [compilePdf() no front]
│
▼
HTTP POST /gerar-pdf (Go)
├─ Cache por hash (md5)
├─ Baixa template (zip) e mantém cache
├─ Pandoc + latexmk (XeLaTeX) + biblatex
└─ PDF de erro (gofpdf) quando falha
-
Editor com linguagem dinâmica (Markdown/YAML/BibTeX) e tema dark.
-
Toolbar para formatação básica de Markdown.
-
Preview HTML sanitizado (Marked + DOMPurify).
-
Modal de PDF com zoom, ajuste à largura, navegação de páginas e sumário (TOC) quando disponível (via
react-pdf). -
Backend Go com:
- Autenticação via
Authorization: Bearer <AUTH_TOKEN>. - Cache por conteúdo (md5 de
indexMd + yaml + bib). - Devolve PDF mesmo em erro, contendo log resumido da compilação.
- CORS habilitado (
Content-Type,Authorization).
- Autenticação via
- Go 1.20+
- Pandoc 2.12+ (recomendado versão recente)
- LaTeX com
xelatex,latexmk,biblatex/bibere fontes DejaVu (opcional, mas recomendado para UTF‑8 no PDF de erro) - Node 18+
Código principal: gerador-pdf-autenticado.go.
| Nome | Obrigatório | Descrição |
|---|---|---|
AUTH_TOKEN |
Sim | Token fixo para autenticação Bearer. O servidor não sobe sem esse valor. |
PORT |
Não | Porta alternativa. O código usa :8080 por padrão (ajuste em listenAddr). |
CORS: o handler já libera
Access-Control-Allow-Origin: *eAuthorizationnos headers.
No Ubuntu/Debian, por exemplo:
sudo apt update
sudo apt install -y pandoc latexmk texlive-xetex texlive-latex-recommended \
texlive-latex-extra texlive-fonts-recommended biber fonts-dejavuexport AUTH_TOKEN="<seu-token-forte>"
go run gerador-pdf-autenticado.go
# Servidor em :8080 → POST /gerar-pdfEndpoint: POST /gerar-pdf
Headers
Content-Type: application/jsonAccept: application/pdfAuthorization: Bearer <AUTH_TOKEN>
Body (JSON)
{
"indexMd": "# Meu artigo...",
"configuracaoYaml": "title: Exemplo\nauthor: Você",
"referenciasBib": "@book{chomsky1957,...}"
}Resposta
-
200 OKcomapplication/pdf(sempre um PDF):- Sucesso: PDF final (ex.:
arquivo.pdf). - Falha: PDF de erro com o log de compilação.
- Sucesso: PDF final (ex.:
-
401 Unauthorized(JSON):{ "error": "não autenticado" }quando o header Bearer é inválido.
O serviço tenta salvar em cache (
work/<hash>/build/arquivo.pdf). Requisições com o mesmo conteúdo retornam do cache.
cURL
curl -X POST http://localhost:8080/gerar-pdf \
-H "Content-Type: application/json" \
-H "Accept: application/pdf" \
-H "Authorization: Bearer $AUTH_TOKEN" \
--data '{
"indexMd":"# Título\\nHello *world*!",
"configuracaoYaml":"title: Demo\\nauthor: Você",
"referenciasBib":""
}' --output saida.pdfCaminho recomendado: import.meta.env com prefixo VITE_.
Arquivos de exemplo:
# .env.development
VITE_API_URL=http://localhost:8080
VITE_API_KEY=SE-O-API-TOKEN-SE-VC-REALMENTE-EXPOR (ver Segurança)
# .env.production
VITE_API_URL=https://sua-api/ # ajuste
Tipos (opcional) em src/env.d.ts:
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_API_KEY?: string;
}
interface ImportMeta { readonly env: ImportMetaEnv }Uso no cliente (ex.: api-client.ts – abordagem recomendada):
const API_URL = import.meta.env.VITE_API_URL;
const API_KEY = import.meta.env.VITE_API_KEY; // evite em produção (ver Segurança)
export async function compilePdf(payload: any, signal?: AbortSignal) {
const res = await fetch(`${API_URL}/gerar-pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/pdf',
...(API_KEY ? { Authorization: `Bearer ${API_KEY}` } : {}),
},
body: JSON.stringify(payload),
signal,
});
const blob = await res.blob();
return { blob };
}Alternativa: usar
definenovite.config.tspara mapearprocess.env.API_URL/API_KEY. Útil se você já tipou dessa forma.
# na pasta do front
npm i
npm run devAbra http://localhost:5173 (porta padrão do Vite).
Editor.tsx: CodeMirror 6, linguagem dinâmica (Markdown/YAML/BibTeX), API porrefcompatível com<textarea>.Toolbar.tsx: ações de formatação (bold, h1/h2/h3, listas, quote, code, link, image, hr).Preview.tsx: exibe HTML já processado (prose, tema claro/escuro).PdfModal.tsx: abre, compila viacompilePdf, exibe PDF com zoom, TOC, navegação, abrir em nova aba e download.Header.tsx: controles de undo/redo, toggle preview, gerar PDF e alternância de tema.FileTabs.tsx: abas de arquivos.api-client.ts:compilePdf(payload)fazPOST /gerar-pdfe retornaBlobdo PDF (ou PDF de erro).
- Não exponha o
AUTH_TOKENdo backend no navegador. Qualquer variávelVITE_*ou valor injetado viadefineé público. - Padrão recomendado: coloque o token apenas no servidor (ex.: um proxy
POST /gerar-pdfem Node/Cloud Run/Functions) que injete o headerAuthorization: Bearer $AUTH_TOKENao falar com o serviço em Go. O front chama seu proxy sem token. - Alternativas: autenticação por usuário (JWT com expiração curta), IAP/Identity-Aware Proxy, ou IAM no provedor de cloud.
-
Erro de compilação: o backend retorna um PDF de erro com log truncado (até ~30KB). Abra-o para ver detalhes.
-
Faltam pacotes LaTeX: instale
latexmk,bibere coleções recomendadas. Em distros minimalistas, o TeX Live padrão pode não incluir biblatex. -
Primeira execução lenta: o servidor baixa o template do GitHub e cacheia (pasta
cache/repo). Depende de rede. -
Worker do
react-pdf: defina o worker corretamente:import { pdfjs } from 'react-pdf'; pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
-
CORS: já habilitado no backend, mas confirme
Authorizationno header se usar proxy/CDN.
.
├── backend/
│ └── gerador-pdf-autenticado.go
├── frontend/
│ ├── src/
│ │ ├── api/
│ │ │ └── api-client.ts
│ │ ├── components/
│ │ │ ├── Editor.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── FileTabs.tsx
│ │ │ ├── Toolbar.tsx
│ │ │ ├── Preview.tsx
│ │ │ └── PdfModal.tsx
│ │ ├── App.tsx
│ │ └── constants.ts
│ ├── index.html
│ ├── vite.config.ts
│ └── ...
└── README.md