# Mindforge ## Visao Geral (Overview) Mindforge e uma ferramenta de estudo para auxiliar na preparacao para concursos publicos brasileiros. O sistema permite validar e gerar materiais de estudo a partir de arquivos Markdown hospedados em repositorios Git, utilizando IA (OpenRouter/OpenAI-compatible API) para processamento. A interface e em **portugues brasileiro** e possui um tema escuro com efeito vidro ("glassy-look"). --- ## Tech Stack ### Frontend (Mindforge.Web) | Tecnologia | Versao | Finalidade | |---|---|---| | **Preact** | ^10.29.0 | UI library (lightweight React alternative) | | **Vite** | ^8.0.1 | Build tool e dev server | | **TypeScript** | ~5.9.3 | Tipagem estatica | | **marked** | ^17.0.4 | Renderizacao Markdown -> HTML | | **diff** | ^8.0.3 | Diff de texto (word-level) | | **Google Fonts (Lato)** | 300/400/700 | Tipografia da interface | ### Backend API (Mindforge.API) | Tecnologia | Versao | Finalidade | |---|---|---| | **.NET 9** | net9.0 | ASP.NET Core Web API | | **C#** | - | Linguagem | | **Microsoft.AspNetCore.OpenApi** | 9.0.12 | Suporte OpenAPI/Swagger | ### Cronjob (mindforge.cronjob) | Tecnologia | Versao | 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 OpenAI-compatible) | --- ## Arquitetura (Architecture) O projeto e um **monorepo** com tres servicos independentes e deployaveis separadamente. ``` ┌─────────────────────────────────────────────────────┐ │ Kubernetes Cluster │ │ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │ │ │ mindforge-web │ │ mindforge-api│ │ cronjob │ │ │ │ (Preact SPA) │──│ (.NET 9 API) │ │ (Go app) │ │ │ │ port 80 │ │ port 8080 │ │ (weekly) │ │ │ └──────────────┘ └──────┬───────┘ └─────┬─────┘ │ │ │ │ │ │ │ mindforge.haven api.mindforge.haven │ │ └──────────────────────────┼─────────────────┼───────┘ │ │ ┌──────┴───────┐ ┌──────┴───────┐ │ OpenRouter │ │ Gitea │ │ (OpenAI API)│ │ (Git repos) │ └──────────────┘ └──────┬───────┘ │ ┌────────┴────────┐ │ Discord │ │ Webhooks │ └─────────────────┘ ``` ### Servicos #### 1. Mindforge.Web (Frontend) - **Tipo**: Single Page Application (SPA) - **Funcao**: Interface do usuario para validacao de arquivos e geracao de flashcards - **Modulos**: Home, Verificador, Flashcards - **Roteamento**: State-based manual via `useState` (nao usa react-router). Componentes sao alternados com `display: block/none`. - **Estado**: Apenas `useState`/`useEffect` locais. Sem store global, sem Context API. #### 2. Mindforge.API (Backend) - **Tipo**: REST API - **Padrao arquitetural**: Clean layered architecture - **Controllers** -> validacao e roteamento HTTP - **Services** -> logica de negocio (FileService, FlashcardService, AgentService, GiteaService) - **Providers** -> abstracao de APIs externas (OpenAIApiProvider implementa ILlmApiProvider) - **Middlewares** -> cross-cutting concerns (ExceptionHandlingMiddleware) - **Models** -> DTOs e tipos de request/response - **DI**: Todos servicos e providers registrados como Scoped - **CORS**: Allow all origins - **Auth**: Nenhuma no momento #### 3. Mindforge.Cronjob (Background Worker) - **Tipo**: Kubernetes CronJob - **Schedule**: Sabados as 9:00 AM - **Pipeline**: 1. Clona repositorio Git via SSH 2. Encontra arquivos modificados nos ultimos 7 dias (`.md`, exceto `Conteudos.md`) 3. Filtra commits de "refactor" via `git log` 4. Ranqueia por linhas alteradas, seleciona top N (default 10) 5. Gera resumo em texto com IA para cada arquivo 6. Formata resumo em Markdown 7. Envia para Discord via webhook (chunked em 1800 chars) - **Error handling**: Reporta ao Haven Notify ### Decisoes de Arquitetura (Architecture Decisions) 1. **Sem banco de dados**: Totalmente stateless. Conteudo de estudo vive em repositorios Git, acessados via Gitea API em tempo de request. 2. **Git como content store**: Nao ha upload de arquivos - o conteudo e lido diretamente do repositorio Git. 3. **IA como engine de processamento**: Toda validacao e geracao e delegada ao LLM via OpenRouter. 4. **Provider abstraction**: `ILlmApiProvider` permite trocar o backend de IA sem alterar a logica de negocio. 5. **Monorepo com deploys independentes**: Cada servico tem seu proprio Dockerfile, manifesto K8s e pipeline CI. 6. **Roteamento manual no frontend**: Sem lib de router, toggle de visibilidade via estado. 7. **Preact em vez de React**: Bundle size menor. --- ## Estrutura do Projeto (Project Structure) ``` mindforge/ ├── AGENTS.md # Instrucoes para agentes AI ├── project-context.md # Este arquivo - fonte da verdade do projeto ├── README.md ├── mindforge.png # Logo do projeto ├── dev.ps1 / dev.sh # Scripts de inicializacao dev ├── .gitea/workflows/ # CI/CD pipelines │ ├── mindforge-api.yaml │ ├── mindforge-web.yaml │ └── mindforge-cronjob.yaml │ ├── Mindforge.API/ # Backend .NET 9 │ ├── Program.cs # Entry point: DI, CORS, middleware │ ├── Controllers/ │ │ ├── FileController.cs # POST /api/v1/file/check │ │ ├── FlashcardController.cs # POST /api/v1/flashcard/generate │ │ └── RepositoryController.cs # GET /api/v1/repository/* │ ├── Services/ # Logica de negocio │ │ ├── AgentService.cs │ │ ├── FileService.cs │ │ ├── FlashcardService.cs │ │ ├── GiteaService.cs │ │ └── Interfaces/ │ ├── Providers/ │ │ ├── ILlmApiProvider.cs # Interface do provedor LLM │ │ └── OpenAIApiProvider.cs # Implementacao OpenAI/OpenRouter com retry │ ├── Middlewares/ │ │ └── ExceptionHandlingMiddleware.cs │ ├── Models/ │ │ ├── FileTreeNode.cs │ │ └── Requests/ # DTOs de request │ ├── Exceptions/ │ │ └── UserException.cs # 400 Bad Request customizado │ └── deploy/ │ └── mindforge-api.yaml # Manifest K8s (Deployment + Service + Ingress) │ ├── Mindforge.Web/ # Frontend Preact + Vite │ ├── vite.config.ts │ ├── tsconfig.app.json # Target ES2023, JSX preact, strict mode │ ├── .env # VITE_API_BASE_URL=http://localhost:5123 │ ├── src/ │ │ ├── main.tsx # Entry point │ │ ├── app.tsx # Componente raiz: header, sidebar, modulos │ │ ├── app.css # Estilos hero, animacoes │ │ ├── index.css # CSS variables, reset, layout base │ │ ├── services/ │ │ │ └── MindforgeApiService.ts # Cliente API (fetch, interfaces tipadas) │ │ └── components/ │ │ ├── Button.tsx / .css # Botao reutilizavel (primary/secondary) │ │ ├── Header.tsx / .css # Header fixo com logo + nome do repo │ │ ├── Sidebar.tsx / .css # Navegacao lateral │ │ ├── VerificadorComponent.tsx / .css # Validador de arquivos │ │ ├── FlashcardComponent.tsx / .css # Gerador de flashcards │ │ └── FileTreeComponent.tsx / .css # Arvore de arquivos do repo │ └── deploy/ │ └── mindforge-web.yaml # Manifest K8s │ └── mindforge.cronjob/ # CronJob em Go ├── cmd/mindforge.cronjob/main.go # Entry point ├── internal/ │ ├── agent/agent.go # Cria e formata resumos │ ├── llm/ # Servico LLM │ ├── git/git.go # Clone, diff, SSH │ ├── errors/errors.go # Reporte de erros │ └── message/messages.go # Notificacoes Discord └── deploy/ └── mindforge-cronjob.yaml # Manifest K8s CronJob ``` --- ## API Endpoints ### File Controller (`/api/v1/file`) | Metodo | Rota | Descricao | |---|---|---| | POST | `/api/v1/file/check` | Valida linguagem ou conteudo de um arquivo | ```json // Request { "fileContent": "string (conteudo do arquivo)", "fileName": "string", "checkType": "language" | "content" } // Response { "result": "string (resultado da validacao)" } ``` ### Flashcard Controller (`/api/v1/flashcard`) | Metodo | Rota | Descricao | |---|---|---| | POST | `/api/v1/flashcard/generate` | Gera flashcards em CSV | ```json // Request { "fileContent": "string", "fileName": "string", "amount": 10, "mode": "Basic" | "Simple" | "Detailed" | "Hyper" } // Response { "result": "string (CSV: Frente;Verso\nFrente;Verso\n...)" } ``` ### Repository Controller (`/api/v1/repository`) | Metodo | Rota | Descricao | |---|---|---| | GET | `/api/v1/repository/info` | Nome do repositorio | | GET | `/api/v1/repository/tree` | Arvore de arquivos do repositorio | | GET | `/api/v1/repository/file?path=...` | Conteudo de um arquivo especifico | --- ## Convencoes de Desenvolvimento (Development Conventions) ### Frontend (Preact/TypeScript) - **Componentes**: Funcoes nomeadas exportadas com `export function Nome()`. Sem `export default`. - **Props**: Interfaces TypeScript definidas no mesmo arquivo do componente. - **Hooks**: `useState`, `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**: Alternancia de componentes via `display: block/none` com estado `activeModule`. Nao usa react-router. - **API**: Chamadas via `MindforgeApiService` (objeto singleton com metodos estaticos usando `fetch`). - **TypeScript**: Strict mode, `erasableSyntaxOnly`, `verbatimModuleSyntax`, `noUnusedLocals`, `noUnusedParameters`. - **Alias**: `react` e `react-dom` mapeados para `preact/compat/` no tsconfig. ### Backend (C#/.NET 9) - **Namespaces**: `Mindforge.API.Controllers`, `Mindforge.API.Services`, etc. - **Interfaces**: Prefixo `I` (ex: `IFileService`, `ILlmApiProvider`). - **DI**: Todos servicos registrados como `Scoped` em `Program.cs`. - **Controllers**: Atributo `[Route("api/v1/...")]`. Metodos retornam `IActionResult`. - **Tratamento de erros**: `ExceptionHandlingMiddleware` captura `UserException` (400) e excecoes genericas (500). ### UI/UX - **Idioma**: Todo texto em **portugues brasileiro**. - **Tema**: Escuro com efeito vidro (glassy). `backdrop-filter: blur()`, fundos `rgba` semitransparentes. - **Botoes**: Estilo iOS-like, modernos. Variantes `primary` (com blur) e `secondary` (transparente). - **Tipografia**: Lato (Google Fonts), pesos 300/400/700. - **Background**: `#005873` (azul petroleo escuro). Nao muito escuro. ### CSS Variables (definidas em `index.css`) ```css --color-bg: #005873; --color-header: #0f0f0f; --color-sidebar: #013a4c; --color-text-creamy: #f4f5f5; --color-accent: #00b4d8; --color-accent-rgb: 0, 180, 216; --color-accent-hover: #0096c7; --font-main: 'Lato', sans-serif; ``` ### Git - Branches: `main` (producao) - CI/CD: Gitea Actions dispara no push para main, build com Docker Buildx multi-arch, push para Gitea Container Registry, deploy no K8s. --- ## Configuracao & Environment Variables ### API (Mindforge.API) | Variavel | Descricao | |---|---| | `OPENAI_API_URL` | URL do endpoint OpenAI-compatible (OpenRouter) | | `OPENAI_TOKEN` | Token de autenticacao da API | | `OPENAI_MODEL` | Modelo LLM a ser usado | | `GITEA_REPO_URL` | URL do repositorio Gitea | | `GITEA_ACCESS_TOKEN` | Token de acesso ao Gitea | ### Web (Mindforge.Web) | Variavel | Descricao | |---|---| | `VITE_API_BASE_URL` | URL base da API (default: `http://localhost:5123`) | ### Cronjob | Variavel | Descricao | |---|---| | `GIT_REPOSITORY` | URL do repo Git a clonar | | `DISCORD_WEBHOOK_URL` | Webhook do Discord para notificacoes | | `OPENAI_API_URL` | URL do endpoint OpenAI | | `OPENAI_TOKEN` | Token de autenticacao | | `HAVEN_NOTIFY_URL` | URL do servico 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/Analise e Engenharia de Dados/Data Lake e Data Warehouse.md - [base]/Concursos/TI/Rede/VLAN.md - [base]/Concursos/TI/Rede/Email/IMAP e POP3.md --- ## Atualizacao - Flashcards Persistidos (2026-05-30) ### Mudancas de Arquitetura - A API deixa de ser totalmente stateless para o dominio de flashcards. - Flashcards agora sao persistidos em PostgreSQL (`mindforge`) via `Dapper` + `Npgsql`. - O schema e criado de forma idempotente no startup da API. ### Tabelas de Flashcard - `flashcard_libraries`: `id`, `file_path` (unico), `file_name`, `subject`, `difficulty`, `card_count`, `created_at`, `updated_at`. - `flashcards`: `id`, `library_id`, `front`, `back`, `position`, `created_at`. ### API de Flashcards (v1) - `POST /api/v1/flashcard/generate` Request: `{ filePaths: string[], amount: 10-30, difficulty: "Easy" | "Hard" }` Comportamento: gera por arquivo selecionado, substitui biblioteca anterior do mesmo `file_path` e retorna bibliotecas com cards. - `GET /api/v1/flashcard/libraries` Lista todas as bibliotecas persistidas, com `subject` para agrupamento na UI. - `GET /api/v1/flashcard/libraries/{id}` Retorna detalhes de uma biblioteca com seus cards. - `POST /api/v1/flashcard/review-session` Request: `{ libraryIds: number[] }` Retorna os cards combinados para sessao de revisao (sem repeticao espaciada nesta fase). ### Frontend - Modulo `Flashcards` atualizado para: - selecionar multiplos arquivos; - definir quantidade fixa por arquivo (10-30); - definir dificuldade (`Facil`/`Dificil`); - gerar e exibir resumo das bibliotecas salvas (sem download CSV). - Novo modulo `Revisao Flashcards`: - lista bibliotecas agrupadas por materia (`subject`); - permite selecionar multiplas bibliotecas; - inicia sessao estilo Anki simplificada (frente, revelar verso, anterior/proximo, progresso visual). ### Novas Configuracoes - `ConnectionStrings:MindforgeDb` para conexao PostgreSQL. - Fallback local/default: `Host=localhost;Port=3307;Database=mindforge;Username=root;Password=root`.