Plataforma local para leitura de Markdown (.md e .mdx) a partir de uma pasta no seu computador. Monorepo com backend Express (TypeScript) e frontend React (Vite + TailwindCSS). Sem banco de dados, sem autenticação — ideal para documentação pessoal ou de equipe na sua máquina.
- Pré-requisitos
- Instalação
- Desenvolvimento
- Produção
- Uso: pasta e leitura
- Funcionalidades
- API
- Estrutura do projeto
- Scripts
- Segurança
- Acesso na rede local
- Variáveis de ambiente
- Licença
- Node.js 22.x (recomendado; versões 20+ devem funcionar)
- npm (ou pnpm/yarn)
Na raiz do repositório:
git clone <url-do-repositorio>
cd docs_server
npm installO npm install na raiz instala as dependências do monorepo e dos pacotes server e client.
npm run devIsso sobe em paralelo:
- API: http://localhost:4000
- Client (Vite): http://localhost:5173
Abra o app em http://localhost:5173. O Vite faz proxy de /api para o servidor; não é preciso configurar CORS no dia a dia.
npm run buildO script:
- Limpa e recria a pasta
./build - Gera o bundle do servidor em
build/index.js(Express + CORS incluídos) - Gera o frontend em
build/static - Cria um
package.jsonembuildcomstart: "node index.js"
cd build
npm startOu a partir da raiz:
npm run start(O start da raiz usa NODE_ENV=production e executa node build/index.js.)
Aplicação disponível em http://localhost:4000 (front e API na mesma origem).
- Primeira vez: ao abrir o app, informe o caminho completo do diretório (ex:
C:\docs,/Users/meu/docs) e clique em Salvar e Carregar. - Trocar pasta: use o botão Trocar pasta no header; no modal, digite o novo path e confirme.
- O path é salvo no localStorage (
md_root_path). A árvore só lista pastas que contenham.mdou.mdxem algum nível. - Sidebar: pastas expandem ao clicar; ao clicar em um arquivo, o conteúdo é carregado no reader.
- Documentos recentes: os últimos arquivos abertos (por pasta) aparecem em Documentos recentes para acesso rápido.
- Árvore de arquivos: apenas
.mde.mdx; pastas vazias de markdown são ocultadas. - Reader Markdown:
- GitHub Flavored Markdown (tabelas, listas de tarefas, etc.)
- Syntax highlight em blocos de código (highlight.js)
- Links e âncoras (markdown-it-anchor)
- Índice (TOC) a partir dos títulos (markdown-it-table-of-contents)
- Código inline com destaque para paths
- Cores em hex no texto renderizadas em chips (ex:
#ff0000)
- Diagramas Mermaid: blocos
```mermaidsão renderizados (flowchart, sequence, etc.) com tema alinhado ao dark/light do app e opção de tela cheia. - Dark/Light mode: toggle no header; preferência salva em
localStorage(theme:"dark"ou"light"). - Acesso na rede: em dev, o Vite e o servidor escutam em
0.0.0.0; você pode acessar de outro dispositivo na mesma rede (ex: celular) viahttp://<IP>:5173ouhttp://<IP>:4000. Ver Acesso na rede local.
| Método | Endpoint | Descrição |
|---|---|---|
GET |
/api/server-info |
Retorna host, port, serverUrl e, em localhost, networkHost (IP da rede) para acesso de outros dispositivos. |
POST |
/api/tree |
Body: { "rootPath": "C:\\docs" }. Retorna { "tree": TreeNode[] } — apenas nós que contêm ou levam a arquivos .md/.mdx. |
POST |
/api/file |
Body: { "rootPath": "...", "relativePath": "pasta/arquivo.md" }. Retorna { "content": string, "relativePath": string } em UTF-8. |
Formato do nó da árvore:
type TreeNode = {
type: "dir" | "file";
name: string;
relativePath: string;
children?: TreeNode[]; // só em type === "dir"
};Path traversal é bloqueado: o arquivo resolvido deve estar dentro de rootPath.
docs_server/
├── client/ # Frontend React + Vite + Tailwind
│ ├── src/
│ │ ├── api.ts # fetchTree, fetchFile, fetchServerInfo
│ │ ├── storage.ts # localStorage: rootPath, theme, recent docs
│ │ ├── markdown.ts # markdown-it + plugins (GFM, anchor, TOC, highlight)
│ │ ├── App.tsx
│ │ ├── main.tsx
│ │ └── components/
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ ├── Reader.tsx
│ │ ├── PathModal.tsx
│ │ ├── FileSearchModal.tsx
│ │ ├── RecentDocs.tsx
│ │ └── MermaidDiagram.tsx
│ ├── vite.config.ts
│ └── package.json
├── server/ # Backend Express + TypeScript
│ ├── src/
│ │ ├── index.ts # rotas /api/tree, /api/file, /api/server-info + static em prod
│ │ └── path-utils.ts # getMarkdownTree, resolveSafe, readFileUtf8
│ └── package.json
├── scripts/
│ └── prepare-build.cjs # orquestra build do server + client → ./build
├── build/ # gerado por npm run build (não versionado)
├── package.json # scripts raiz: dev, build, start
├── README.md
├── LICENSE # GPL-3.0
├── NETWORK.md # dicas de acesso na rede (firewall, mesma Wi‑Fi)
└── .gitignore
| Script | Descrição |
|---|---|
npm run dev |
Sobe server e client em paralelo (concurrently). |
npm run build |
Gera ./build (server bundle + client estático). |
npm run build:no-check |
Mesmo build sem tsc (apenas para acelerar se você confiar no código). |
npm run start |
NODE_ENV=production node build/index.js — serve o app na porta 4000. |
Dentro de server/ e client/ há scripts próprios (dev, build, etc.); o uso normal é pelos scripts da raiz.
- Path traversal: o backend garante que o arquivo solicitado esteja dentro do
rootPath. Requisições com..ou caminhos que saiam do root retornam 403. - Uso local: não há autenticação nem banco. O
rootPathé sempre enviado pelo frontend; não há sessão no servidor. O app foi pensado para uso na sua máquina ou rede interna. - CORS: habilitado para facilitar desenvolvimento; em produção você pode restringir a origem se quiser.
- Variáveis de ambiente: não commite
.envou.env.local. Eles estão no.gitignore. Ver Variáveis de ambiente.
Para abrir o app em outro dispositivo (celular, outro PC) na mesma rede:
- Dev: use o endereço Network que o Vite mostra (ex:
http://192.168.1.6:5173). - Produção: use
http://<IP-do-servidor>:4000.
Se aparecer ERR_CONNECTION_REFUSED:
- Reinicie o ambiente (
npm run devounpm run start). - Confirme que o Vite/servidor está escutando em
0.0.0.0(já configurado no projeto). - Firewall (macOS/Windows/roteador): libere as portas 5173 (dev) e 4000 (prod) para conexões na LAN.
- Celular e PC devem estar na mesma rede Wi‑Fi.
Mais detalhes em NETWORK.md.
O projeto não exige arquivo .env para rodar. Se no futuro você usar variáveis de ambiente:
- Não commite
.envou.env.local(eles já estão no.gitignore). - Use
.env.example(sem valores reais) para documentar as chaves necessárias e adicione.env.exampleao repositório.
Este projeto está sob a licença GNU General Public License v3.0 (GPL-3.0). Veja o arquivo LICENSE para o texto completo.