{"id":28918797,"url":"https://github.com/vakesz/notification_app","last_synced_at":"2026-05-08T14:48:15.064Z","repository":{"id":300109036,"uuid":"1005227614","full_name":"vakesz/notification_app","owner":"vakesz","description":"A real-time web application for monitoring blog posts with intelligent notifications, Azure AD authentication, and web push support.","archived":false,"fork":false,"pushed_at":"2025-06-19T22:27:21.000Z","size":126,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-19T23:25:41.489Z","etag":null,"topics":["azure","blog","flask","python"],"latest_commit_sha":null,"homepage":"https://vakesz.github.io/notification_app/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vakesz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-19T22:20:14.000Z","updated_at":"2025-06-19T22:26:12.000Z","dependencies_parsed_at":"2025-06-19T23:36:23.878Z","dependency_job_id":null,"html_url":"https://github.com/vakesz/notification_app","commit_stats":null,"previous_names":["vakesz/notification_app"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vakesz/notification_app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakesz%2Fnotification_app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakesz%2Fnotification_app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakesz%2Fnotification_app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakesz%2Fnotification_app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vakesz","download_url":"https://codeload.github.com/vakesz/notification_app/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vakesz%2Fnotification_app/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261226440,"owners_count":23127319,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","blog","flask","python"],"created_at":"2025-06-22T02:39:10.992Z","updated_at":"2026-05-08T14:48:15.045Z","avatar_url":"https://github.com/vakesz.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Notification App](https://repository-images.githubusercontent.com/1005227614/0c6f83ac-3ce5-4ee8-bcad-344c166ad5fb)\n\n# Notification App\n\n[![Build Status](https://github.com/vakesz/notification_app/actions/workflows/ci.yml/badge.svg)](https://github.com/vakesz/notification_app/actions)\n[![Docker Image](https://github.com/vakesz/notification_app/actions/workflows/release-docker.yml/badge.svg)](https://github.com/vakesz/notification_app/actions/workflows/release-docker.yml)\n[![GitHub Pages](https://github.com/vakesz/notification_app/actions/workflows/static.yml/badge.svg)](https://vakesz.github.io/notification_app/)\n[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![Linting: Ruff](https://img.shields.io/badge/linting-ruff-46a2f1.svg)](https://github.com/astral-sh/ruff)\n\nA real-time web application for monitoring blog posts with intelligent notifications, Azure AD authentication, and web push support.\n\n## Description\n\nThis Flask-based application automatically monitors blog content and delivers personalized notifications to users. It features Microsoft Azure AD single sign-on authentication, customizable notification filters, and web push notifications for real-time updates.\n\n### Key Features\n\n- **Real-time Blog Monitoring**: Automatically polls blog APIs and parses new posts\n- **Azure AD Authentication**: Secure single sign-on with Microsoft accounts\n- **Intelligent Filtering**: Location-based and keyword-based notification filters\n- **Web Push Notifications**: Cross-platform push notifications with VAPID support\n- **User Dashboard**: Comprehensive interface for managing settings and viewing notifications\n- **Multi-language Support**: English, Hungarian, and Swedish language options\n- **Responsive Design**: Mobile-friendly web interface\n- **Export Functionality**: Export posts and notifications to JSON format\n- **Session Management**: Secure session handling with token validation\n\n## Installation Instructions\n\n### Prerequisites\n\n- Python 3.10 or higher\n- Azure AD application registration\n- VAPID keys for web push notifications\n\n### Local Development Setup\n\n1. **Clone the repository**\n\n   ```bash\n   git clone https://github.com/vakesz/notification_app.git\n   cd notification_app\n   ```\n\n2. **Create a virtual environment**\n\n   ```bash\n   python -m venv venv\n   source venv/bin/activate  # On Windows: venv\\Scripts\\activate\n   ```\n\n3. **Install dependencies**\n\n   ```bash\n   pip install -e .\n   ```\n\n4. **Environment Configuration**\n\n   Create a `.env` file in the project root:\n\n   ```env\n   # Flask Configuration\n   SECRET_KEY=your-secret-key-here\n   FLASK_ENV=development\n\n   # Azure AD Configuration\n   AAD_CLIENT_ID=your-azure-ad-client-id\n   AAD_CLIENT_SECRET=your-azure-ad-client-secret\n   AAD_TENANT_ID=your-azure-ad-tenant-id\n   AAD_REDIRECT_URI=http://localhost:5000/auth/callback\n\n   # Blog API Configuration\n   BLOG_API_URL=https://your-blog-api-url.com\n   BLOG_API_AUTH_METHOD=none\n\n   # Optional Blog API Authentication (choose one if needed)\n   # For OAuth2:\n   # BLOG_API_OAUTH2_CLIENT_ID=your-oauth2-client-id\n   # BLOG_API_OAUTH2_CLIENT_SECRET=your-oauth2-client-secret\n   # BLOG_API_OAUTH2_TOKEN_URL=https://your-oauth2-token-url.com\n\n   # For MSAL:\n   # BLOG_API_MSAL_CLIENT_ID=your-msal-client-id\n   # BLOG_API_MSAL_CLIENT_SECRET=your-msal-client-secret\n   # BLOG_API_MSAL_TENANT_ID=your-msal-tenant-id\n   # BLOG_API_MSAL_SCOPE=your-msal-scope\n\n   # For NTLM:\n   # BLOG_API_NTLM_USER=your-ntlm-user\n   # BLOG_API_NTLM_PASSWORD=your-ntlm-password\n   # BLOG_API_NTLM_DOMAIN=your-ntlm-domain\n\n   # For Cookie (session cookie on target blog domain):\n   # BLOG_API_AUTH_METHOD=cookie\n   # Option A: Multiple cookies (recommended when the site sets more than one)\n   # BLOG_API_COOKIES=name1=value1; name2=value2\n   # Option B: Single cookie (fallback)\n   # BLOG_API_COOKIE_NAME=your-cookie-name\n   # BLOG_API_COOKIE_VALUE=your-cookie-value\n   # BLOG_API_COOKIE_DOMAIN=your-blog-domain.com   # optional, defaults to BLOG_API_URL host\n   # BLOG_API_COOKIE_PATH=/                         # optional, defaults to '/'\n\n   # Web Push Configuration\n   PUSH_VAPID_PUBLIC_KEY=your-vapid-public-key\n   PUSH_VAPID_PRIVATE_KEY=your-vapid-private-key\n   PUSH_CONTACT_EMAIL=your-contact-email@example.com\n\n   # Token Encryption (required)\n   # Generate with: python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\"\n   TOKEN_ENCRYPTION_KEY=base64-urlsafe-32-byte-fernet-key\n\n   # Application Settings\n   APP_NAME=Blog Notifications Parser\n   APP_DATABASE_PATH=db/posts.db\n   POLLING_INTERVAL_MINUTES=15\n   HTTP_TIMEOUT=30\n\n   # Optional Advanced Settings\n   # HTTP_MAX_RETRIES=3\n   # HTTP_RETRY_BACKOFF=1\n   # POLLING_BACKOFF_FACTOR=1.5\n   # POLLING_MAX_BACKOFF=3600\n   # AUTH_TOKEN_TTL_DAYS=30\n   # PUSH_TTL=86400\n   ```\n\n5. **Database Setup**\n\n   ```bash\n   mkdir -p db\n   # Database will be automatically initialized on first run\n   ```\n\n### Docker Deployment\n\n#### Run with Docker\n\n```bash\n# Pull the latest image\ndocker pull ghcr.io/vakesz/notification_app:latest\n\n# Run with environment variables\ndocker run -d \\\n  --name notification-app \\\n  -p 5000:5000 \\\n  -e SECRET_KEY=your-secret-key \\\n  -e AAD_CLIENT_ID=your-azure-ad-client-id \\\n  -e AAD_CLIENT_SECRET=your-azure-ad-client-secret \\\n  -e AAD_TENANT_ID=your-azure-ad-tenant-id \\\n  -e BLOG_API_URL=https://your-blog-api-url.com \\\n  -e PUSH_VAPID_PUBLIC_KEY=your-vapid-public-key \\\n  -e PUSH_VAPID_PRIVATE_KEY=your-vapid-private-key \\\n  -e PUSH_CONTACT_EMAIL=your-contact-email@example.com \\\n  -e TOKEN_ENCRYPTION_KEY=your-fernet-key \\\n  -v notification-db:/app/db \\\n  ghcr.io/vakesz/notification_app:latest\n```\n\n#### Run with Docker Compose\n\n```yaml\nversion: \"3.8\"\nservices:\n  app:\n    image: ghcr.io/vakesz/notification_app:latest\n    ports:\n      - \"5000:5000\"\n    environment:\n      - SECRET_KEY=your-secret-key\n      - AAD_CLIENT_ID=your-client-id\n      - AAD_CLIENT_SECRET=your-azure-ad-client-secret\n      - AAD_TENANT_ID=your-tenant-id\n      - AAD_REDIRECT_URI=http://localhost:5000/auth/callback\n      - BLOG_API_URL=https://your-blog-api-url.com\n      - PUSH_VAPID_PUBLIC_KEY=your-vapid-public-key\n      - PUSH_VAPID_PRIVATE_KEY=your-vapid-private-key\n      - PUSH_CONTACT_EMAIL=your-contact-email@example.com\n      - TOKEN_ENCRYPTION_KEY=your-fernet-key\n    volumes:\n      - notification-db:/app/db\nvolumes:\n  notification-db:\n```\n\n**Note:** The Docker image uses multi-architecture support (amd64/arm64) and runs with `gunicorn -w 4 -b 0.0.0.0:5000 \"app.web.main:create_app()\"` to properly initialize the Flask application factory.\n\n## Usage Instructions\n\n### Starting the Application\n\n**Development Mode:**\n\n```bash\nexport FLASK_APP=app.web.main\nexport FLASK_ENV=development\nflask run\n```\n\n**Production Mode:**\n\n```bash\ngunicorn -w 4 -b 0.0.0.0:5000 \"app.web.main:create_app()\"\n```\n\n### Accessing the Application\n\n1. Navigate to `http://localhost:5000`\n2. Click \"Login with Microsoft\" to authenticate\n3. Configure your notification preferences in the dashboard\n4. Enable push notifications when prompted by your browser\n\n### API Endpoints\n\nThe application provides several API endpoints:\n\n- `POST /api/subscriptions` - Subscribe to push notifications\n- `DELETE /api/subscriptions` - Unsubscribe from push notifications\n- `GET /api/notifications/status` - Get notification summary\n- `POST /api/notifications/mark-read` - Mark notifications as read\n- `POST /api/notifications/settings` - Update notification settings\n- `GET /api/session/validate` - Validate current session\n\n### Configuration Options\n\n**Notification Settings:**\n\nUsers can customize their notification preferences through the dashboard:\n\n- **Language**: Choose from English, Hungarian, or Swedish\n- **Location Filter**: Filter notifications by specific locations (only receive notifications for selected locations)\n- **Keyword Filter**: Filter notifications by custom keywords (only receive notifications containing specified keywords)\n- **Push Notifications**: Enable/disable web push notifications (respects user opt-out preferences)\n\n**Intelligent Notification Filtering:**\n\nThe application implements intelligent filtering to ensure users only receive relevant notifications:\n\n- **Location-based Filtering**: Users can select specific locations and will only receive notifications for posts from those locations\n- **Keyword-based Filtering**: Users can define custom keywords and will only receive notifications for posts containing those keywords\n- **Push Opt-out**: Users can disable push notifications while keeping other notification types active\n- **Targeted Delivery**: Push notifications are only sent to users whose filters match the post content, respecting individual preferences\n- **Per-User Read State**: Each user has their own read/unread status for notifications, allowing independent tracking\n\n**Blog API Authentication:**\n\nThe application supports multiple authentication methods:\n\n- OAuth2 client credentials\n- Microsoft MSAL authentication\n- NTLM authentication\n- Cookie (session cookie supplied via environment)\n- No authentication (public APIs)\n\n## Architecture Overview\n\n```text\n├── app/\n│   ├── api/routes/          # Flask blueprints and routes\n│   ├── core/               # Core configuration and security\n│   ├── db/                 # Database models and management\n│   ├── services/           # Business logic services\n│   ├── utils/              # Utility functions\n│   └── web/                # Web application entry point\n├── static/                 # Frontend assets\n├── templates/              # Jinja2 templates\n└── db/                     # SQLite database files\n```\n\n### Key Components\n\n- **AuthService**: Handles Azure AD authentication and token management\n- **PollingService**: Monitors blog APIs for new content\n- **NotificationService**: Manages user notifications and web push with intelligent filtering\n- **DatabaseManager**: Handles all database operations including user-targeted push subscriptions\n- **ContentParser**: Parses HTML content from blog APIs\n\n### Notification Filtering System\n\nThe application implements a sophisticated notification filtering system that ensures users only receive relevant content:\n\n1. **Two-Stage Filtering**:\n   - Location-based filtering: Users can specify which locations they're interested in\n   - Keyword-based filtering: Users can define keywords that must be present in posts\n\n2. **Targeted Push Delivery**:\n   - Push notifications are only sent to users whose filters match the post content\n   - The system queries for push subscriptions of matched users only, not all users\n   - Individual push notification preferences are respected (users can opt-out of push while keeping other notifications)\n\n## Development\n\n### Development Dependencies\n\nThe project includes development tools configured in `pyproject.toml`:\n\n- **pytest**: Testing framework with coverage reporting\n- **ruff**: Linting, import sorting, and code formatting\n\nAll development dependencies are automatically installed with:\n\n```bash\npip install -e .[dev]\n```\n\n### Running Tests and Quality Checks\n\n```bash\n# Install development dependencies\npip install -e .[dev]\n\n# Run tests\npytest\n\n# Run with coverage\npytest --cov=app --maxfail=1 --disable-warnings -q\n```\n\n### Code Quality\n\nThe project uses automated code quality tools that are also run in CI:\n\n```bash\n# Format code (Black-compatible)\nruff format app/\n\n# Check formatting only\nruff format --check .\n\n# Lint and sort imports (includes isort via rule I)\nruff check .\n\n# Autofix lint and import issues\nruff check --fix .\n\n# Only sort imports (if needed)\nruff check --select I --fix .\n```\n\n### Continuous Integration\n\nThis project uses GitHub Actions for automated testing and code quality checks. The CI pipeline:\n\n- **Multi-Python Testing**: Tests against Python 3.10, 3.11, and 3.12\n- **Code Formatting**: Validates code formatting with Ruff\n- **Import Sorting**: Ensures consistent import organization with Ruff (isort rules)\n- **Linting**: Code quality checks with Ruff\n- **Test Coverage**: Automated test execution with coverage reporting\n\nThe CI workflow runs on every push to main and on pull requests, ensuring code quality and compatibility across Python versions.\n\n## 🤝 Contribution Guidelines\n\nContributions are welcome. Open an issue or create a pull request.\n\n### Getting Started\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feature/your-feature-name`\n3. Make your changes following the coding standards below\n4. Write tests for new functionality\n5. Submit a pull request with a clear description\n\n### Coding Standards\n\n- **Python Code**: Follow PEP 8 style guidelines\n- **Type Hints**: Use type annotations for all functions\n- **Documentation**: Include docstrings for all classes and functions\n- **Logging**: Use the logging module instead of print statements\n- **Error Handling**: Implement comprehensive error handling\n\n### Reporting Issues\n\n- Use the GitHub issue tracker\n- Include detailed reproduction steps\n- Provide environment information (Python version, OS, etc.)\n- Include relevant log files when possible\n\n## License Information\n\nThis project is licensed under the MIT License.\n\n### Third-Party Dependencies\n\nThis project uses several open-source libraries:\n\n- Flask (BSD-3-Clause)\n- MSAL (MIT)\n- pywebpush (MIT)\n- APScheduler (MIT)\n- Beautiful Soup (MIT)\n\nSee `pyproject.toml` for a complete list of dependencies.\n\n### Support\n\n- **Question**: Use [Discussions](https://github.com/vakesz/notification_app/discussions)\n- **Bug Reports**: [GitHub Issues](https://github.com/vakesz/notification_app/issues)\n- **Security Issues** (Confidential): Use the [Security Policy](https://github.com/vakesz/notification_app/blob/main/SECURITY.md)\n\nBuilt with ❤️ for efficient company communication.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvakesz%2Fnotification_app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvakesz%2Fnotification_app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvakesz%2Fnotification_app/lists"}