# Mindforge ## Visão Geral Mindforge é uma ferramenta de estudo para auxiliar na preparação para concursos públicos brasileiros. O sistema permite validar e gerar materiais de estudo a partir de arquivos Markdown hospedados em repositórios Git, utilizando IA (API compatível com OpenRouter/OpenAI) para processamento. A interface é em **português brasileiro** e possui um tema claro quente com estética vintage acadêmica — tons de creme, pergaminho e dourado com texturas de papel, seguindo as diretrizes em `GUIDELINES-ESTILO.md`. --- ## Stack Tecnológica ### Frontend (Mindforge.Web) | Tecnologia | Versão | Finalidade | |---|---|---| | **Preact** | ^10.29.0 | Biblioteca de interface (alternativa leve ao React) | | **Vite** | ^8.0.1 | Ferramenta de build e servidor de desenvolvimento | | **TypeScript** | ~5.9.3 | Tipagem estática | | **marked** | ^17.0.4 | Renderização Markdown -> HTML | | **diff** | ^8.0.3 | Diff de texto (word-level) | > Nota: `marked` v17+ inclui tipos TypeScript nativos. `@types/marked` foi removido. `@types/diff` v7 permanece em devDependencies como fallback de tipos. | **Google Fonts (Inter)** | 400..950 (variável) | Tipografia da interface | ### Backend API (Mindforge.API) | Tecnologia | Versão | Finalidade | |---|---|---| | **.NET 9** | net9.0 | ASP.NET Core Web API | | **C#** | - | Linguagem | | **Microsoft.AspNetCore.OpenApi** | 9.0.12 | Suporte OpenAPI/Swagger | | **Dapper** | 2.1.66 | Acesso a dados do domínio de flashcards | | **Npgsql** | 9.0.4 | Driver PostgreSQL para persistência de flashcards | ### Cronjob (mindforge.cronjob) | Tecnologia | Versão | Finalidade | |---|---|---| | **Go** | 1.22 | Linguagem | | **godotenv** | v1.5.1 | Carregamento de arquivos .env | ### Infraestrutura | Tecnologia | Finalidade | |---|---| | **Docker** | Imagens multi-arch (amd64, arm64) | | **Kubernetes** | Orquestracao (Deployments, CronJobs, Services, Ingress) | | **Gitea** | Git hosting + Container registry (`git.ivanch.me`) | | **Gitea Actions** | CI/CD (build & deploy no push para main) | | **Nginx Ingress** | K8s ingress controller | | **OpenRouter API** | Provedor LLM (endpoint compatível com OpenAI) | --- ## Arquitetura O projeto é um monorepo com três serviços independentes. - `Mindforge.Web`: SPA em Preact/Vite com navegação manual por estado e sem `react-router`. - `Mindforge.API`: REST API em camadas com Controllers, Services, Repositories, Providers, Middlewares e Models; o domínio de flashcards é persistido em PostgreSQL (`mindforge`) via `Dapper` + `Npgsql`, com schema criado no startup. - `mindforge.cronjob`: worker em Go executado semanalmente para resumir alterações recentes em arquivos `.md`. - Integrações principais: OpenRouter para IA, Gitea como repositório de conteúdo e Discord via webhook para notificações. - Deploy: cada serviço possui Dockerfile, manifesto Kubernetes e pipeline independente no Gitea Actions. - Decisões-chave: Git como content store, revisão espaçada calculada em leitura e Preact em vez de React. --- ## Estrutura do Projeto - `mindforge/`: raiz do projeto com documentação, scripts, logo e workflows do Gitea. - `Mindforge.API/`: backend .NET 9 com controllers, services, repositories, providers, models e deploy Kubernetes. - `Mindforge.Web/`: frontend Preact + Vite com componentes de interface, cliente de API e deploy Kubernetes. - `mindforge.cronjob/`: worker em Go com lógica de IA, Git, erros e mensagens do Discord, além do deploy Kubernetes. --- ## Endpoints da API ### Controller de Arquivos (`/api/v1/file`) | Método | Rota | Descrição | |---|---|---| | POST | `/api/v1/file/check` | Valida a linguagem ou o conteúdo de um arquivo | ```json // Request { "fileContent": "string (conteudo do arquivo)", "fileName": "string", "checkType": "language" | "content" } // Response { "result": "string (resultado da validacao)" } ``` ### Controller de Flashcards (`/api/v1/flashcard`) | Método | Rota | Descrição | |---|---|---| | POST | `/api/v1/flashcard/generate` | Gera ou substitui bibliotecas de flashcards a partir dos arquivos selecionados | | GET | `/api/v1/flashcard/libraries` | Lista todas as bibliotecas persistidas | | GET | `/api/v1/flashcard/libraries/{id}` | Retorna uma biblioteca específica com seus cards | | POST | `/api/v1/flashcard/review-session` | Inicia uma sessão de revisão com bibliotecas selecionadas | | POST | `/api/v1/flashcard/review-answer` | Registra se a resposta do card estava correta | | GET | `/api/v1/flashcard/rag-status` | Retorna o dashboard de revisão espaçada por matéria/submatéria | Formas de requisição principais: - `POST /api/v1/flashcard/generate`: `{ filePaths: string[], amount: 10-30, difficulty: "Easy" | "Hard" }` - `POST /api/v1/flashcard/review-session`: `{ libraryIds: number[] }` - `POST /api/v1/flashcard/review-answer`: `{ cardId: number, correct: boolean }` ### Controller de Repositório (`/api/v1/repository`) | Método | Rota | Descrição | |---|---|---| | GET | `/api/v1/repository/info` | Nome do repositório | | GET | `/api/v1/repository/tree` | Árvore de arquivos do repositório | | GET | `/api/v1/repository/file?path=...` | Conteúdo de um arquivo específico | --- ## Convenções de Desenvolvimento ### Frontend (Preact/TypeScript) - **Componentes**: Funções nomeadas exportadas com `export function Nome()`. Sem `export default`. - **Props**: Interfaces TypeScript definidas no mesmo arquivo do componente. - **Hooks**: `useState` e `useEffect` importados de `preact/hooks`. - **Children**: Tipados como `ComponentChildren` de `preact`. - **CSS**: Um arquivo `.css` por componente, importado diretamente. **Sem CSS Modules.** - **Estado**: Apenas estado local (`useState`). Sem store global, sem Context API. - **Roteamento**: Alternância de componentes via `display: block/none` com o estado `activeModule`. Não usa `react-router`. - **API**: Chamadas via `MindforgeApiService` (objeto singleton com métodos assíncronos usando `fetch`). - **TypeScript**: Modo estrito, `erasableSyntaxOnly`, `verbatimModuleSyntax`, `noUnusedLocals`, `noUnusedParameters`. - **Alias**: `react` e `react-dom` mapeados para `preact/compat/` no tsconfig. - **Layout**: `.app` usa CSS Grid (`grid-template-columns: 288px 1fr`) com Sidebar sticky + `.main` contendo Topbar e área de conteúdo. ### Backend (C#/.NET 9) - **Namespaces**: `Mindforge.API.Controllers`, `Mindforge.API.Services`, etc. - **Interfaces**: Prefixo `I` (ex.: `IFileService`, `ILlmApiProvider`). - **DI**: Todos os serviços registrados como `Scoped` em `Program.cs`. `HttpClient` obtido via `IHttpClientFactory` (não injetado diretamente). - **Controllers**: Atributo `[Route("api/v1/...")]`. Métodos retornam `IActionResult`. - **Tratamento de erros**: `ExceptionHandlingMiddleware` captura `UserException` (400) e exceções genéricas (500). ### UI/UX - **Idioma**: Todo texto em **português brasileiro**. - **Tema**: Claro quente vintage ("mesa de estudo pessoal"). Fundos creme/pergaminho/dourado com textura de papel (grid via `body::before`). Vidro translúcido com `backdrop-filter` em tons quentes. - **Botões**: Estilo pill quente com sombras marrons. Variantes `primary` (gradiente azul, com brilho hover) e `secondary` (translúcido pergaminho). - **Tipografia**: Inter (UI global), Georgia/Times New Roman (marca e cabeçalhos), Segoe UI/Inter (conteúdo do flashcard). - **Background**: Gradiente radial quente (dourado/azul) sobre base `#fff8e6` com overlay de grid de papel 24px. - **Layout**: CSS Grid (`grid-template-columns: 288px minmax(0, 1fr)`). Sidebar sticky com textura diagonal. Topbar integrada ao fluxo (não fixa). - **Animações**: Apenas sob ação do usuário (flip 3D do flashcard, carimbo, saída do cartão, confete canvas). Sem animações infinitas ou spinners. - **Flashcard**: Cartão 3D com efeito `rotateY(180deg)`, frente papel pautado com borda tracejada, verso azulado. Carimbo de feedback (correto/incorreto). Confete canvas ao acertar. - **Responsivo**: Breakpoints em 1120px (sidebar colapsada) e 760px (layout single-column). ### Variáveis CSS (definidas em `index.css`) ```css --bg: #fbf3df; --bg-soft: #fff9ea; --paper: #fffaf0; --paper-deep: #f2dfb3; --ink: #2b241c; --muted: #7b6a50; --line: rgba(82, 60, 28, 0.18); --blue: #3f7cac; --blue-deep: #255f8d; --green: #4f8f5a; --green-deep: #32683b; --red: #b75b4d; --red-deep: #8d3c32; --gold: #c79539; --violet: #7e65a8; --shadow: 0 28px 70px rgba(58, 42, 17, 0.18); --card-shadow: 0 30px 90px rgba(76, 48, 12, 0.20); --radius-xl: 32px; --radius-lg: 22px; --radius-md: 16px; --ease: cubic-bezier(.2,.75,.2,1); ``` ### Git e Scripts de Build/Deploy - Branches: `main` (produção) - CI/CD: Gitea Actions dispara no push para `main`, faz build com Docker Buildx multi-arch, envia para o Gitea Container Registry e faz o deploy no K8s. - Build Local: `re-build.ps1` detecta Docker local; se ausente, envia o projeto via SSH para `iris.haven` e executa `docker buildx bake --load` remotamente. Se Docker CLI presente, usa `DOCKER_HOST` para fallback remoto. - Deploy Local: `re-deploy.ps1` executa o build das imagens e aplica as atualizações no cluster Kubernetes. - API Dockerfile: mapeia `TARGETARCH=amd64` para `x64` (arquitetura esperada pelo .NET). --- ## Configuração e Variáveis de Ambiente ### API (Mindforge.API) | Variável | Descrição | |---|---| | `OPENAI_API_URL` | URL do endpoint compatível com OpenAI (OpenRouter) | | `OPENAI_TOKEN` | Token de autenticação da API | | `OPENAI_MODEL` | Modelo LLM a ser usado | | `GITEA_REPO_URL` | URL do repositório Gitea | | `GITEA_ACCESS_TOKEN` | Token de acesso ao Gitea | | `ConnectionStrings:MindforgeDb` | Conexão PostgreSQL usada pelo domínio de flashcards | Fallback local/padrão para o banco de flashcards: `Host=localhost;Port=3307;Database=mindforge;Username=root;Password=root`. ### Web (Mindforge.Web) | Variável | Descrição | |---|---| | `VITE_API_BASE_URL` | URL base da API (padrão: `http://localhost:5123`) | ### Cronjob | Variável | Descrição | |---|---| | `GIT_REPOSITORY` | URL do repositório Git a clonar | | `DISCORD_WEBHOOK_URL` | Webhook do Discord para notificações | | `OPENAI_API_URL` | URL do endpoint OpenAI | | `OPENAI_TOKEN` | Token de autenticação | | `HAVEN_NOTIFY_URL` | URL do serviço de reporte de erros | ## Repositório base para os arquivos A base do repositório para os arquivos de estudo (compartilhado pelo Obsidian) é https://git.ivanch.me/ivanch/concurso. Ele segue o padrão `[base]/concurso/[Matéria]/[SubMatéria*]/[Arquivo]`, onde [base] é a URL do Git, `concurso` é o nome da pasta dentro do Git base, `Matéria` é o nome da matéria (ex.: `Direito Constitucional`), `SubMatéria` é o nome da submatéria (ex.: `Poder Legislativo`) - podendo ser opcional e com múltiplas submatérias - e `Arquivo` é o nome do arquivo (ex.: `Câmara dos Deputados.md`). Exemplos: - [base]/Concursos/Direito Constitucional/Poder Legislativo/Câmara dos Deputados.md - [base]/Concursos/Direito Constitucional/Poder Legislativo/CPIs.md - [base]/Concursos/Direito Constitucional/Ciência, Meio Ambiente e Índios.md - [base]/Concursos/Direito Constitucional/Ciência, Meio Ambiente e Índios.md - [base]/Concursos/TI/Análise e Engenharia de Dados/Data Lake e Data Warehouse.md - [base]/Concursos/TI/Rede/VLAN.md - [base]/Concursos/TI/Rede/E-mail/IMAP e POP3.md