TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
AI-powered email analysis directly inside Gmail — summary, category, priority, and reply suggestions in one click.
Chrome Extension: submitted to the Chrome Web Store — link coming soon after review.
BriskMail is an AI-powered email analysis tool that integrates directly into Gmail via a Chrome extension. Open any email, click Analyze Email, and get:README 전문 보기 ↗AI-powered email analysis directly inside Gmail — summary, category, priority, and reply suggestions in one click.
Web app: email-classifier-ruddy.vercel.app
Chrome Extension: submitted to the Chrome Web Store — link coming soon after review. Meanwhile, you can load it unpacked.
Privacy policy: email-classifier-ruddy.vercel.app/privacy.html
BriskMail is an AI-powered email analysis tool that integrates directly into Gmail via a Chrome extension. Open any email, click Analyze Email, and get:
Supports Portuguese and English — switch languages anytime from the extension popup. The web app also accepts .txt, .eml, and .pdf uploads (up to 5 MB).
Gmail (Chrome Extension) Web app (Vercel)
content_script.js reads the email body text → POST /api/v1/analyze
background.js → POST /api/v1/analyze file → POST /api/v1/classify-file
↘ ↙
FastAPI backend (Fly.io)
routes.py → input validation, HTTP error mapping
FileParser → .txt / .eml / .pdf extraction (≤ 5 MB)
EmailAnalyzer
1. language-aware TTL cache (100 entries, 1 h)
2. AI call with the shared JSON schema
(transport retry: 3 attempts, exponential backoff)
3. strict JSON parse + validation
4. up to 2 repair calls with safe structural feedback
↓
OpenAI Structured Outputs (production) / Ollama `format` (local dev)
email-classifier/
├── app/
│ ├── api/routes.py # REST endpoints and HTTP error mapping
│ ├── models/schemas.py # Pydantic models + shared analysis JSON schema
│ ├── services/
│ │ └── analyzer.py # EmailAnalyzer: cache → AI call → bounded repair → validation
│ ├── utils/
│ │ ├── ai_client.py # AIClient ABC + OllamaClient + OpenAIClient + factory
│ │ ├── ai_response.py # Sanitized diagnostics and repair feedback for invalid AI responses
│ │ └── file_parser.py # .txt / .eml / .pdf extraction (pypdf)
│ ├── config.py # Pydantic Settings (env-based, Docker-aware)
│ ├── exceptions.py # Domain exceptions for upstream AI failures
│ └── main.py # FastAPI app, CORS, request logging middleware
├── extension/ # Chrome Extension (Manifest V3)
│ ├── manifest.json # Permissions, host_permissions, web_accessible_resources
│ ├── content_script.js # Gmail DOM injection, MutationObserver, PT/EN UI
│ ├── background.js # Service worker — intermediates API calls
│ ├── popup/ # Extension popup (status, PT/EN toggle, how-to)
│ ├── panel/ # Styles for the result panel injected inside Gmail
│ └── assets/ # Icons 16/48/128px, promo tile
├── frontend/ # Web SPA (Vercel)
│ ├── index.html
│ ├── js/app.js
│ ├── css/style.css
│ ├── privacy.html # Privacy policy (required for Chrome Web Store)
│ ├── vercel.json # Vercel deploy config
│ └── assets/icon.png # Brand icon
├── tests/
│ ├── conftest.py # Fixtures + offline network guard
│ ├── test_analyzer.py
│ ├── test_ai_client.py
│ ├── test_ai_response.py
│ ├── test_api_routes.py
│ ├── test_file_parser.py
│ ├── test_deploy_health.py # Public health smoke script
│ ├── test_deploy_workflow.py # CI/CD and rollback policy checks
│ ├── test_security_workflows.py # Dependabot / CodeQL / dependency review checks
│ └── extension_background.test.js # Node test for the extension service worker
├── scripts/ci/ # Container smoke, public health probe, rollback image validator
├── docs/ # Decisions log, CI/CD, deploy/rollback and security runbooks
├── .github/workflows/ # ci.yml (quality gates + deploy), fly-rollback.yml, codeql.yml, dependency-review.yml
├── docker-compose.yml
├── Dockerfile # Multi-stage build, non-root runtime user
├── fly.toml # Fly.io app config + /health service check
└── requirements.txt
| Method | Path | Description |
|---|---|---|
POST | /api/v1/analyze | Analyzes raw email text. Used by the Chrome extension and the web app |
POST | /api/v1/classify-file | Analyzes an uploaded .txt, .eml, or .pdf file (multipart, field file, ≤ 5 MB) |
GET | /health | Liveness check ({"status":"healthy"}) — used by Fly service checks and CI smoke tests |
GET | /api/v1/health | Analysis service health |
GET | /test-ai | Manual connectivity check against the configured AI provider (not a health check) |
Request
POST /api/v1/analyze
{
"email_content": "Oi João, a reunião do projeto Alpha foi movida para sexta às 14h. Confirme sua presença. Abs, Maria.",
"language": "pt"
}
email_content requires at least 10 characters; language is "pt" (default) or "en".
Response
{
"summary": "Maria informa que a reunião do projeto Alpha foi reagendada para sexta às 14h e pede confirmação.",
"category": "Reunião / Agenda",
"priority": "normal",
"action_required": true,
"suggestions": [
{
"title": "Confirmar presença",
"content": "Oi Maria, confirmado! Estarei presente na sexta às 14h. Abs, João.",
"tone": "cordial"
}
]
}
category, priority (alta / normal / baixa), and tone (formal / cordial / casual / técnico) are fixed enums. suggestions is empty when no reply is needed.
Errors
| Status | When |
|---|---|
400 | Invalid upload (empty, over 5 MB, unsupported extension, malformed multipart), or the AI provider rejected the call (e.g. OpenAI authentication or rate limit) |
422 | Request body fails schema validation (e.g. email_content too short, unknown language) |
502 | The AI provider returned a response that could not be validated, even after repair attempts. The detail is sanitized — raw model output is never exposed |
500 | Unexpected server error |
Interactive docs: Swagger UI at /docs, ReDoc at /redoc.
The full, append-only log lives in docs/DECISIONS.md.
Provider-agnostic AI layer — AIClient ABC with OllamaClient (local, free) and OpenAIClient (production). Switch via the AI_PROVIDER env var — no code changes.
Provider-native structured output — the same canonical JSON schema is sent to both providers: Ollama receives it through format, OpenAI through strict response_format.json_schema. The analyzer accepts only a complete JSON object; text wrappers are rejected instead of being extracted with a regular expression.
Bounded repair loop — if parsing or validation fails, EmailAnalyzer makes at most 2 additional calls, repeating the original request plus a safe structural diagnostic (e.g. a missing field). Raw model output is never fed back, logged in production, or returned to clients. This logical repair is separate from the transport retry handled by tenacity, and only validated results are cached.
Language-aware cache — cache key is SHA-256(language + email_content), so PT and EN analyses for the same email are cached independently.
DOM-based Gmail integration — the extension reads email content via div.a3s.innerText and uses MutationObserver to detect newly opened emails. No OAuth required for the MVP.
Focused API surface — text analysis uses POST /api/v1/analyze; file uploads use POST /api/v1/classify-file with the same EmailAnalyzer response contract. The legacy binary /api/v1/classify flow was removed and now returns 404.
Local development uses Ollama; OpenAI is only used in production.
qwen2.5:3b pulledollama pull qwen2.5:3b
The API container reaches Ollama on the host through the Docker bridge, so Ollama must listen on all interfaces:
sudo systemctl edit ollama
# [Service]
# Environment="OLLAMA_HOST=0.0.0.0:11434"
sudo systemctl daemon-reload && sudo systemctl restart ollama
docker-compose.yml sets OLLAMA_BASE_URL=http://172.21.0.1:11434 (the bridge gateway on the original dev machine). If your gateway differs, check it after the first docker compose up and update the compose file:
docker network inspect email-classifier_default --format '{{(index .IPAM.Config 0).Gateway}}'
git clone https://github.com/Brunotlps/email-classifier.git
cd email-classifier
docker compose up -d --build
curl http://localhost:8001/health
curl http://localhost:8001/test-ai
Compose already sets AI_PROVIDER=ollama and the model. The source is baked into the image (no volumes), so rebuild with docker compose up -d --build after code changes.
curl -X POST http://localhost:8001/api/v1/analyze \
-H "Content-Type: application/json" \
-d '{"email_content": "Prezado, gostaria de agendar uma reunião para discutir o projeto.", "language": "pt"}'
API docs: Swagger UI at http://localhost:8001/docs · ReDoc at http://localhost:8001/redoc
To run outside Docker, copy .env.example to .env; the settings are read by app/config.py.
| Variable | Default | Description |
|---|---|---|
ENVIRONMENT | development | production disables response previews in invalid-AI-response logs |
AI_PROVIDER | ollama | ollama or openai |
OLLAMA_BASE_URL | http://localhost:11434 | localhost ↔ host.docker.internal is auto-adjusted; overridden by docker-compose.yml |
OLLAMA_MODEL | qwen2.5:3b | Ollama model name |
OPENAI_API_KEY | — | Required only when AI_PROVIDER=openai; must start with sk- |
OPENAI_MODEL | gpt-4o-mini (in .env.example) | Must support Structured Outputs (response_format: json_schema) — set it explicitly, the code fallback gpt-3.5-turbo does not |
ALLOWED_ORIGINS | http://localhost:3000,http://localhost:5173 | Comma-separated CORS origins |
MAX_TOKENS | 500 | Max tokens per AI response |
TEMPERATURE | 0.7 | AI generation temperature |
The test suite is fully offline: tests/conftest.py selects an unreachable Ollama URL, clears the OpenAI key, and blocks IP connections and DNS resolution, so every AI call must be mocked. The Docker image contains only app/, so run the tests from a local environment — the same commands CI uses:
python3.11 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python -m pytest # full Python suite
python -m pytest --cov=app --cov-report=term # with coverage
python -m pytest tests/test_analyzer.py -v # single file
node --test tests/extension_background.test.js # extension service worker
Container smoke test (offline, --network none):
docker build --tag briskmail-ci:local .
bash scripts/ci/container-smoke.sh briskmail-ci:local
See CI quality gates for the full set of local checks, including actionlint.
ci.yml runs on every PR to main and every push to main, with no path filters: Python tests (3.11), extension checks (Node 24 tests, JS syntax, manifest parsing), and a Docker build + offline /health smoke. A ci-gate job passes only when all three succeed and is the required check on main.main, the deploy job runs only after ci-gate passes, for the same commit SHA, and waits for manual approval of the Production environment. Images are tagged with the commit SHA and verified with a public /health smoke after release. See Fly production delivery.Production approval. The runbook is in Fly production delivery.pip and github-actions), dependency review on PRs (fails on high/critical vulnerabilities), and CodeQL for Python and JavaScript. All actions are pinned to full commit SHAs. See supply chain security and the multipart upload fix.chrome://extensionsextension/ folderThe unpacked extension calls the production API (https://email-classifier-api.fly.dev).
| Document | Contents |
|---|---|
docs/DECISIONS.md | Architecture decisions log (ADR-lite, append-only) |
docs/ci-quality-gates.md | CI jobs, local equivalents, offline test boundary |
docs/fly-production-deploy.md | Gated deploy, rollback runbook, health checks, token rotation |
docs/security-supply-chain.md | Dependabot, dependency review, CodeQL, alert triage (PT-BR) |
docs/multipart-security-fix.md | python-multipart upgrade and upload hardening |
| Component | Status |
|---|---|
| FastAPI backend | Live on Fly.io |
| Web frontend | Live on Vercel |
| Chrome Extension | v1.2.1 — submitted to the Chrome Web Store, publication pending |
| PT/EN support | Complete |
| CI/CD | Required ci-gate, approval-gated Fly deploy, manual rollback |
| Supply chain security | Dependabot, dependency review, and CodeQL active |
Next
Medium term
Long term
Built and maintained by Bruno Teixeira Lopes — a backend developer from Brazil.