03 - Usando uma lib para Markdown
pnpm install remark remark-parse remark-html
pnpm install --save-dev @types/remark-parse
Ver depois as Libs:
https://github.com/remarkjs/remark-rehype
https://github.com/rehypejs/rehype-highlight
https://github.com/rehypejs/rehype-sanitize
Helper para converter markdown -> HTML
Crie lib/markdown.ts
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkHtml from 'remark-html';
export async function markdownToHtml(markdown: string): Promise<string> {
const file = await unified()
.use(remarkParse)
.use(remarkHtml)
.process(markdown);
return String(file);
}
Criar app/docs/[...slug]/page.tsx
import { supabaseServer } from '@/lib/supabaseServer';
import { supabaseAdmin } from '@/lib/supabaseAdmin';
import { markdownToHtml } from '@/lib/markdown';
import { redirect, notFound } from 'next/navigation';
interface DocPageProps {
params: { slug: string[] };
}
export default async function DocPage({ params }: DocPageProps) {
const slug = params.slug.join('/'); // 'financeiro/relatorio-mensal'
const supabase = supabaseServer();
// 1) autenticação
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
redirect('/login');
}
// 2) buscar doc que o usuário PODE ver (RLS + grupos + pastas)
const { data: doc, error } = await supabase
.from('app_docs')
.select('id, title, bucket, storage_path')
.eq('slug', slug)
.maybeSingle();
if (!doc || error) {
notFound(); // sem permissão ou não existe
}
// 3) baixar markdown do Storage
const { data: file, error: sError } = await supabaseAdmin
.storage
.from(doc.bucket)
.download(doc.storage_path);
if (!file || sError) {
console.error(sError);
notFound();
}
const rawMarkdown = await file.text();
// 4) converter markdown -> HTML
const html = await markdownToHtml(rawMarkdown);
// 5) renderizar
return (
<article className="prose mx-auto p-8">
<h1>{doc.title}</h1>
<div
className="mt-4"
dangerouslySetInnerHTML={{ __html: html }}
/>
</article>
);
}
O risco de dangerouslySetInnerHTML aqui é mitigado pelo fato de:
- conteúdo ser seu (markdown controlado por você)
- não estar vindo de usuário externo malicioso
Se quiser ainda mais segurança, dá pra plugar um sanitizer (ex: rehype-sanitize).
Resumo
Você agora tem:
- 🔐 Auth com Supabase (sem auto-cadastro, você controla usuários)
- 🧑🤝🧑 Grupos (
app_groups) - 📁 Permissão por pasta (
app_group_foldersvia prefixo doslug) - 📄 Permissão por doc (
app_group_docs) - 🗂️ Docs mapeados em
app_docs→ apontam para arquivos no Supabase Storage - 🧑💻 Painéis:
/admin/groups→ gerencia pastas por grupo/admin/docs→ gerencia docs específicos por grupo
- 📝
/docs/[...slug]:- verifica user
- consulta doc com RLS (grupo + pasta)
- baixa markdown do Storage
- renderiza bonitinho com Markdown