A secure, Python-based web application for exporting GitHub issues to Excel with customizable field mappings and parent relationship tracking.
- 🔐 Secure Authentication - OAuth-based social sign-in
- 🔒 Encrypted Token Storage - GitHub tokens encrypted with AES-256-GCM
- 📊 Excel Export - Download issues as formatted Excel files
- 🎯 Custom Field Mappings - Choose which issue fields to export
- 🔗 Parent Relationships - Automatically resolve parent issue references
- 👥 Multi-User Support - Each user has isolated configurations and credentials
- ⚡ Fast & Modern - Built with FastAPI + HTMX for optimal performance
- Backend: FastAPI (Python 3.11+)
- Frontend: HTMX + Tailwind CSS + Alpine.js
- Database: MySQL/MariaDB with SQLAlchemy ORM
- Security: AES-256-GCM encryption, JWT authentication
- Excel Generation: openpyxl
- Storage: AWS S3 (optional)
- Python 3.11 or higher
- MySQL/MariaDB database
- GitHub Personal Access Token with
reposcope - OAuth provider credentials (for authentication)
- AWS S3 credentials (optional, for file storage)
git clone https://github.com/mmockd/Gexport-python.git
cd Gexport-pythonpython3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activatepip install -r requirements.txtCopy the example environment file and configure it:
cp .env.example .envEdit .env and set your values:
# Database
DATABASE_URL=mysql+pymysql://user:password@localhost:3306/github_exporter
# Security (generate a secure random key)
SECRET_KEY=your-secret-key-min-32-characters-long
# OAuth (configure your OAuth provider)
OAUTH_CLIENT_ID=your-oauth-client-id
OAUTH_CLIENT_SECRET=your-oauth-client-secret
OAUTH_AUTHORIZE_URL=https://oauth-provider.com/authorize
OAUTH_TOKEN_URL=https://oauth-provider.com/token
OAUTH_USER_INFO_URL=https://oauth-provider.com/userinfo
OAUTH_REDIRECT_URI=http://localhost:8000/auth/callback
# AWS S3 (optional)
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
S3_BUCKET=github-exporter-files# Run Alembic migrations
alembic upgrade head# Development mode
python -m app.main
# Or with uvicorn
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000The application will be available at http://localhost:8000
Navigate to the application and click "Sign In" to authenticate via OAuth.
- Go to GitHub Settings → Tokens
- Create a new token with the repo scope
- Copy the token
- Navigate to Credentials page in the app
- Paste and save your token
Your token is encrypted using AES-256-GCM before storage.
- Go to Configuration page
- Enter the repository owner and name (e.g.,
facebook/react) - Click "Add Repository"
- The repository will be validated and set as active
- Go to Field Mappings page
- Enable/disable fields you want in your export
- Drag to reorder fields
- Available fields include:
- Issue number, title, state
- Created, updated, closed dates
- Author, assignees, labels
- Milestone, comments, URL
- Parent issue information
- Go to Export page
- Choose issue state (open/closed/all)
- Enable "Resolve Parent Relationships" if needed
- Click "Preview Issues" to see what will be exported
- Click "Export to Excel" to download
gexport-python/
├── app/
│ ├── api/ # API endpoints
│ │ ├── auth.py # Authentication
│ │ ├── config.py # Repository configuration
│ │ ├── credentials.py # Token management
│ │ ├── fields.py # Field mappings
│ │ └── export_routes.py # Export functionality
│ ├── core/ # Core utilities
│ │ ├── auth.py # Auth dependencies
│ │ ├── config.py # Settings
│ │ ├── database.py # Database connection
│ │ ├── excel.py # Excel generation
│ │ ├── github.py # GitHub API integration
│ │ ├── security.py # Encryption & JWT
│ │ └── storage.py # S3 storage
│ ├── models/ # Database models
│ │ └── models.py # SQLAlchemy models
│ ├── templates/ # HTML templates
│ │ ├── base.html
│ │ ├── index.html
│ │ ├── config.html
│ │ ├── credentials.html
│ │ ├── fields.html
│ │ └── export.html
│ ├── static/ # Static files
│ └── main.py # FastAPI application
├── alembic/ # Database migrations
├── tests/ # Tests
├── requirements.txt # Python dependencies
├── .env.example # Environment template
└── README.md # This file
GitHub Personal Access Tokens are encrypted before storage using:
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Derivation: PBKDF2-SHA256 with 100,000 iterations
- Random Components: Unique salt and nonce per encryption
- Master Key: Derived from
SECRET_KEYenvironment variable
The encryption process:
- Generate random 16-byte salt
- Derive 256-bit encryption key using PBKDF2
- Generate random 12-byte nonce
- Encrypt token using AES-GCM
- Store:
base64(salt + nonce + ciphertext + auth_tag)
Tokens are never displayed in full. Only a masked version is shown:
- Format:
ghp_****...****wxyz - Shows first 4 and last 4 characters only
- Each user's data is completely isolated
- Configurations, credentials, and exports are user-specific
- Database queries are filtered by user ID
- No cross-user data access possible
- Use strong SECRET_KEY: Minimum 32 characters, randomly generated
- Enable HTTPS: In production, always use HTTPS
- Rotate tokens: Periodically regenerate GitHub tokens
- Limit token scope: Only grant necessary permissions
- Monitor access: Review export history regularly
GET /auth/login- Redirect to OAuth providerGET /auth/callback- OAuth callbackPOST /auth/logout- Sign outGET /auth/me- Get current user
GET /api/config/- List configurationsPOST /api/config/- Create configurationPUT /api/config/{id}/activate- Activate configurationDELETE /api/config/{id}- Delete configurationGET /api/config/active- Get active configuration
GET /api/credentials/- Get credential statusPOST /api/credentials/- Save tokenDELETE /api/credentials/- Delete token
GET /api/fields/- List field mappingsGET /api/fields/available- List available fieldsPUT /api/fields/{id}- Update field mappingPOST /api/fields/bulk-update- Bulk updatePOST /api/fields/reset- Reset to defaults
POST /api/export/preview- Preview issuesPOST /api/export/excel- Export to ExcelGET /api/export/history- Get export history
Run the test suite:
pytestRun with coverage:
pytest --cov=app tests/Create a new migration:
alembic revision --autogenerate -m "Description"Apply migrations:
alembic upgrade headRollback migration:
alembic downgrade -1# Format with black
black app/ tests/
# Sort imports
isort app/ tests/- Set
DEBUG=Falsein.env - Use a production-grade database
- Enable HTTPS
- Use a reverse proxy (nginx/Apache)
- Set up proper logging
- Configure S3 for file storage
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]# Test database connection
python -c "from app.core.database import engine; print(engine.connect())"- Ensure token has
reposcope - Check if token is expired
- Verify token hasn't been revoked
- Verify
OAUTH_REDIRECT_URImatches your OAuth app configuration - Check client ID and secret are correct
- Ensure OAuth provider URLs are accessible
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
MIT License - see LICENSE file for details
For issues or questions:
- Open an issue on GitHub
- Contact: [your-email]
- Advanced filtering (labels, milestones, date ranges)
- Scheduled exports
- CSV export option
- Custom parent relationship patterns
- Bulk repository operations
- Export templates
- API rate limit handling
- Webhook integration