-
-
Notifications
You must be signed in to change notification settings - Fork 131
feat: Standalone App Packaging & Setup Flow #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
karan-vk
wants to merge
34
commits into
Gururagavendra:main
Choose a base branch
from
karan-vk:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 16 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
3f7ef6f
refactor: use platformdirs for data storage and allow setup mode
karan-vk 371c446
ci: add github actions build workflow
karan-vk 38c3446
chore: update gitignore for test and tool artifacts
karan-vk 72decff
feat: implement setup api endpoint for credentials upload
karan-vk ca2cd94
feat: add frontend setup view for missing credentials
karan-vk 9a5a0e8
docs: add agent documentation and analysis files
karan-vk 518c4e1
ci: remove macos-13 runner due to configuration error
karan-vk 58adc1f
ci: add auto-release on tag push
karan-vk 2dac02c
docs: update README with standalone app capabilities
karan-vk 6fafb57
ci: optimize build workflow and update release action
karan-vk 68fa67b
docs: fix formatting and language identifiers in local AGENTS.md guides
karan-vk aa9dc1c
docs: update main AGENTS.md with platformdirs and setup route
karan-vk 3bd2214
refactor: sort API router exports alphabetically
karan-vk ecedcf4
fix(backend): security improvements and robust setup logic
karan-vk f319145
fix(build): enhance cross-platform packaging
karan-vk 0eaa416
fix(frontend): improve file upload UX and validation
karan-vk 497f57a
docs: update README based on CodeRabbit feedback
karan-vk 0499370
docs: add community governance and improve documentation
karan-vk 7590827
chore: remove AGENTS.md from git tracking
karan-vk 735ccbc
feat(ui): redesign setup flow with multi-step wizard
karan-vk bb87e8f
feat(core): add custom exception hierarchy and error handling utilities
karan-vk 7202818
feat(config): add performance settings for large inbox optimization
karan-vk 1e05128
feat(scan): implement streaming mode for large inbox support
karan-vk a27f36c
refactor(gmail): integrate error handling decorators across services
karan-vk f97fbd1
refactor(api): improve error responses with structured error codes
karan-vk e0e1d82
feat(ui): add toast notification system for user feedback
karan-vk 2371c45
refactor(ui): integrate notifications into existing UI components
karan-vk 7559984
test(integration): add comprehensive integration test suite
karan-vk 321a2ce
docs(roadmap): mark in-progress items as completed
karan-vk 33cec6b
docs(readme): update release artifact filenames to match CI output
karan-vk 66f53ac
chore: move GMAIL_SERVICES_ANALYSIS.md to .github/agent/reference/
karan-vk 79922a0
fix(build): correct python_multipart module name in hiddenimports
karan-vk e00e186
feat(ui): improve setup wizard with missing credential steps
karan-vk 3327cf2
docs(ui): add setup video tutorial link to wizard
karan-vk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| name: Build | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| tags: [ 'v*' ] | ||
| pull_request: | ||
| branches: [ main ] | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build on ${{ matrix.os }} | ||
| runs-on: ${{ matrix.os }} | ||
| permissions: | ||
| contents: write | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| # macos-13 is the last Intel runner | ||
| # macos-14 is Apple Silicon (M1) | ||
| os: [ubuntu-latest, windows-latest, macos-14] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v5 | ||
|
|
||
| - name: Set up Python | ||
| run: uv python install 3.12 | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync | ||
|
|
||
| - name: Build with PyInstaller | ||
| run: uv run python build_app.py | ||
|
|
||
| - name: Zip Build Output (Unix) | ||
| if: runner.os != 'Windows' | ||
| run: | | ||
| cd dist | ||
| zip -r ../gmail-cleaner-${{ matrix.os }}.zip gmail-cleaner/ | ||
|
|
||
| - name: Zip Build Output (Windows) | ||
| if: runner.os == 'Windows' | ||
| run: | | ||
| Compress-Archive -Path dist/gmail-cleaner -DestinationPath gmail-cleaner-${{ matrix.os }}.zip | ||
|
|
||
| - name: Upload Artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: gmail-cleaner-${{ matrix.os }} | ||
| path: gmail-cleaner-${{ matrix.os }}.zip | ||
| if-no-files-found: error | ||
| retention-days: 30 | ||
|
|
||
| - name: Release | ||
| uses: softprops/action-gh-release@v2 | ||
| if: startsWith(github.ref, 'refs/tags/') | ||
| with: | ||
| files: gmail-cleaner-${{ matrix.os }}.zip | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| # AGENTS.md - Development Guide | ||
|
|
||
| **Generated:** Thu, Feb 12, 2026 | ||
| **Branch:** main | ||
|
|
||
| ## OVERVIEW | ||
| Privacy-focused Gmail cleanup tool using **FastAPI** (Python 3.9+) and **Vanilla JS**. Runs locally/Docker, no external database. | ||
|
|
||
| ## STRUCTURE | ||
| ``` | ||
| . | ||
| ├── app/ # FastAPI backend | ||
| │ ├── api/ # Routes (actions.py, status.py) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| │ ├── core/ # Config & State (Global variables) | ||
| │ ├── models/ # Pydantic schemas | ||
| │ └── services/ # Business logic | ||
| │ ├── auth.py # OAuth flow & Credential mgmt (God Object) | ||
| │ └── gmail/ # Gmail API operations (Scan, Delete, Archive) | ||
| ├── static/ # Frontend (Vanilla JS) | ||
| │ ├── js/ # Logic (Module pattern, global namespace) | ||
| │ └── css/ # Styles | ||
| ├── templates/ # HTML (Jinja2) | ||
| └── tests/ # Pytest suite | ||
| ├── unit/ # Isolated tests (Mocked) | ||
| └── integration/ # [EMPTY] Missing integration tests | ||
| ``` | ||
|
|
||
| ## WHERE TO LOOK | ||
| | Task | Location | Notes | | ||
| |------|----------|-------| | ||
| | **Auth/Login** | `app/services/auth.py` | Handles OAuth, tokens, local server. Complex. | | ||
| | **Email Logic** | `app/services/gmail/` | Modular operations (scan, delete, etc.). | | ||
| | **Frontend UI** | `static/js/` | Ad-hoc framework. `main.js` orchestrates. | | ||
| | **State** | `app/core/state.py` | Global thread-safe state for async tasks. | | ||
| | **API Routes** | `app/api/` | `actions.py` (POST), `status.py` (GET), `setup.py` (Setup). | | ||
| | **Config** | `app/core/config.py` | Settings via Pydantic (`.env`). | | ||
|
|
||
| ## CODE MAP (Key Symbols) | ||
| | Symbol | Type | Location | Role | | ||
| |--------|------|----------|------| | ||
| | `GmailService` | Class | `app/services/auth.py` | Wrapper for Gmail API resource. | | ||
| | `get_gmail_service` | Func | `app/services/auth.py` | **CRITICAL**. Retrieves/refreshes auth. | | ||
| | `process_scan` | Func | `app/services/gmail/scan.py` | Core scanning logic. | | ||
| | `GmailCleaner` | Obj | `static/js/main.js` | Global frontend namespace (State/UI). | | ||
| | `app` | Var | `app/main.py` | FastAPI application instance. | | ||
|
|
||
| ## CONVENTIONS | ||
| - **State**: Backend uses `app.core.state` global variables for progress tracking. | ||
| - **Frontend**: "Module Pattern" with global `GmailCleaner`. No build step. | ||
| - **Async**: Backend is async (FastAPI), but uses `threading` for background tasks. | ||
| - **Testing**: | ||
| - `pytest` with `pytest-asyncio`. | ||
| - **Mocks**: Aggressive mocking of `credentials.json` in `conftest.py`. | ||
| - **Fixtures**: `mock_gmail_auth` (autouse) prevents real auth in tests. | ||
|
|
||
| ## ANTI-PATTERNS (THIS PROJECT) | ||
| - **NO SECRETS**: NEVER commit `credentials.json`, `token.json`, or `.env`. | ||
| - **No `try/except`**: Use `HTTPException` or custom exceptions. | ||
| - **Frontend Coupling**: Logic/UI tightly coupled in `labels.js`/`delete.js`. | ||
| - **Global State**: Frontend relies on global `window.GmailCleaner`. | ||
|
|
||
| ## COMMANDS | ||
| ```bash | ||
| # Dev | ||
| uv sync | ||
| uv run python main.py | ||
|
|
||
| # Test | ||
| uv run pytest | ||
| uv run pytest tests/unit/services/auth/ | ||
|
|
||
| # Lint | ||
| uv run ruff check . | ||
| uv run pyright | ||
| ``` | ||
|
|
||
| ## NOTES | ||
| - **Auth Flow**: Uses a local server callback. Docker requires port mapping. | ||
| - **Data Persistence**: `token.json` stored in `./data/` (Docker volume) or platform-specific data dir via `platformdirs`. | ||
| - **Concurrency**: `app/services/gmail` uses batch requests (performance). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Deep Dive Analysis: `app/services/gmail` | ||
|
|
||
| ## 1. Overview | ||
| The `app/services/gmail` directory is well-structured and modular. It follows a clear separation of concerns, with each file handling a specific aspect of Gmail operations (scanning, deleting, archiving, etc.). The `__init__.py` file acts as a clean facade, exposing necessary functions while hiding internal implementation details. | ||
|
|
||
| ## 2. Pattern Consistency | ||
| The codebase demonstrates high consistency in the following areas: | ||
|
|
||
| * **State Management**: All modules consistently use the global `state` object from `app.core.state` to track progress, store results, and handle errors. This ensures a unified way of communicating status to the frontend. | ||
| * **Service Retrieval**: Every module retrieves the Gmail service using `get_gmail_service()` from `app.services.auth`, handling potential errors in a uniform way. | ||
| * **Batch Processing**: Operations like scanning and deleting consistently use Gmail API's batch capabilities (`new_batch_http_request` or `batchModify`) to optimize performance and respect rate limits. | ||
| * **Status Reporting**: Each module provides a `get_<operation>_status()` function that returns a copy of the relevant state, maintaining a consistent interface for status polling. | ||
| * **Error Handling**: `try-except` blocks are used consistently to catch exceptions, update the state with the error message, and mark the operation as done. | ||
|
|
||
| ## 3. Shared Helpers | ||
| `app/services/gmail/helpers.py` is effectively used to share common logic, preventing code duplication. Key helpers include: | ||
| * `build_gmail_query`: Centralizes query construction logic. | ||
| * `get_sender_info` & `get_subject`: Standardizes header parsing. | ||
| * `get_unsubscribe_from_headers`: Encapsulates the logic for finding unsubscribe links. | ||
| * `validate_unsafe_url`: Provides shared security validation. | ||
|
|
||
| ## 4. "God Objects" & Complexity | ||
| While most files are focused, a few areas show signs of high complexity: | ||
|
|
||
| * **`app/services/auth.py` (External to `gmail` dir but critical)**: This file is a "God Object" candidate (685 lines). The `get_gmail_service` function contains a massive nested `run_oauth` function that handles the entire OAuth flow, including starting a local HTTP server. This logic should ideally be extracted into a separate `OAuthManager` class or module. | ||
| * **`app/services/gmail/delete.py`**: This file (439 lines) handles both the *scanning* of senders for deletion and the *actual deletion* (both single sender and bulk). Splitting this into `delete_scan.py` and `delete_action.py` would improve maintainability and separation of concerns. | ||
|
|
||
| ## 5. Circular Dependencies | ||
| **Status: Clean.** | ||
| No circular dependencies were found. The dependency graph is unidirectional and healthy: | ||
| * `app/services/gmail/*` imports `app.core.state`, `app.services.auth`, and `app.services.gmail.helpers`. | ||
| * `app/services/auth` imports `app.core.state` and `app.core.settings`. | ||
| * `app/core/state` has no internal dependencies. | ||
| * `app/services/gmail/__init__.py` imports from submodules but submodules do not import back from `__init__`. | ||
|
|
||
| ## 6. Recommendations | ||
| 1. **Refactor `app/services/auth.py`**: Extract the OAuth flow logic into a dedicated handler to reduce the size and complexity of the auth service. | ||
| 2. **Split `app/services/gmail/delete.py`**: Separate the scanning logic (`scan_senders_for_delete`) from the deletion logic (`delete_emails_by_sender`, `delete_emails_bulk`) to align with the single-responsibility principle. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| """ | ||
| Setup API Routes | ||
| ---------------- | ||
| Endpoints for initial application setup (uploading credentials). | ||
| """ | ||
|
|
||
| import json | ||
| import logging | ||
| import os | ||
|
|
||
| from fastapi import APIRouter, File, HTTPException, UploadFile, status | ||
|
|
||
| from app.core import settings | ||
|
|
||
| router = APIRouter(prefix="/api", tags=["Setup"]) | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| @router.post("/setup") | ||
| async def setup_credentials(file: UploadFile = File(...)): | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| """Upload credentials.json file.""" | ||
| # Check if credentials already exist | ||
| if os.path.exists(settings.credentials_file): | ||
| raise HTTPException( | ||
| status_code=status.HTTP_409_CONFLICT, | ||
| detail="Credentials file already exists. Please delete it manually to upload a new one.", | ||
| ) | ||
|
|
||
| try: | ||
| content = await file.read() | ||
|
|
||
| # Validate JSON structure | ||
| try: | ||
| data = json.loads(content) | ||
| except json.JSONDecodeError: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_400_BAD_REQUEST, | ||
| detail="Invalid JSON file", | ||
| ) | ||
|
|
||
| # Validate content (must be Google OAuth credentials) | ||
| if "installed" not in data and "web" not in data: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_400_BAD_REQUEST, | ||
| detail="Invalid credentials file. Must contain 'installed' or 'web' client configuration.", | ||
| ) | ||
|
|
||
| # Ensure directory exists | ||
| os.makedirs( | ||
| os.path.dirname(os.path.abspath(settings.credentials_file)), exist_ok=True | ||
| ) | ||
|
|
||
| # Save to settings.credentials_file | ||
| with open(settings.credentials_file, "wb") as f: | ||
| f.write(content) | ||
|
|
||
| logger.info(f"Credentials uploaded successfully to {settings.credentials_file}") | ||
| return { | ||
| "message": "Credentials uploaded successfully", | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| except HTTPException: | ||
| raise | ||
| except Exception as e: | ||
| logger.exception("Error uploading credentials") | ||
| raise HTTPException( | ||
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||
| detail="Failed to upload credentials. Please check server logs.", | ||
| ) from e | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.