# Munich News Daily 📰 A TLDR/Morning Brew-style news email platform specifically for Munich. Get the latest Munich news delivered to your inbox every morning. ## Features - 📧 Email newsletter subscription system - 📰 Aggregated news from multiple Munich news sources - 🎨 Beautiful, modern web interface - 📊 Subscription statistics - 🔄 Real-time news updates ## Tech Stack - **Backend**: Python (Flask) - Modular architecture with blueprints - **Frontend**: Node.js (Express + Vanilla JavaScript) - **Database**: MongoDB - **News Crawler**: Standalone Python microservice - **News Sources**: RSS feeds from major Munich news outlets ## Setup Instructions ### Prerequisites - Python 3.8+ - Node.js 14+ - npm or yarn - Docker and Docker Compose (recommended for MongoDB) OR MongoDB (local installation or MongoDB Atlas account) ### Backend Setup 1. Navigate to the backend directory: ```bash cd backend ``` 2. Create a virtual environment (recommended): ```bash python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate ``` 3. Install dependencies: ```bash pip install -r requirements.txt ``` 4. Set up MongoDB using Docker Compose (recommended): ```bash # From the project root directory docker-compose up -d ``` This will start MongoDB in a Docker container. The database will be available at `mongodb://localhost:27017/` **Useful Docker commands:** ```bash # Start MongoDB docker-compose up -d # Stop MongoDB docker-compose down # View MongoDB logs docker-compose logs -f mongodb # Restart MongoDB docker-compose restart mongodb # Remove MongoDB and all data (WARNING: deletes all data) docker-compose down -v ``` **Alternative options:** - **Local MongoDB**: Install MongoDB locally and make sure it's running - **MongoDB Atlas** (Cloud): Create a free account at [mongodb.com/cloud/atlas](https://www.mongodb.com/cloud/atlas) and get your connection string 5. Create a `.env` file in the backend directory: ```bash # Copy the template file cp env.template .env ``` Then edit `.env` with your configuration: ```env # MongoDB connection (default: mongodb://localhost:27017/) # For Docker Compose (no authentication): MONGODB_URI=mongodb://localhost:27017/ # For Docker Compose with authentication (if you modify docker-compose.yml): # MONGODB_URI=mongodb://admin:password@localhost:27017/ # Or for MongoDB Atlas: # MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/ # Email configuration (optional for testing) SMTP_SERVER=smtp.gmail.com SMTP_PORT=587 EMAIL_USER=your-email@gmail.com EMAIL_PASSWORD=your-app-password # Ollama Configuration (for AI-powered features) # Remote Ollama server URL OLLAMA_BASE_URL=http://your-remote-server-ip:11434 # Optional: API key if your Ollama server requires authentication # OLLAMA_API_KEY=your-api-key-here # Model name to use (e.g., llama2, mistral, codellama, llama3) OLLAMA_MODEL=llama2 # Enable/disable Ollama features (true/false) OLLAMA_ENABLED=false ``` **Notes:** - For Gmail, you'll need to use an [App Password](https://support.google.com/accounts/answer/185833) instead of your regular password. - For Ollama, replace `your-remote-server-ip` with your actual server IP or domain. Set `OLLAMA_ENABLED=true` to enable AI features. 6. Run the backend server: ```bash python app.py ``` The backend will run on `http://localhost:5001` (port 5001 to avoid conflict with AirPlay on macOS) ### Frontend Setup 1. Navigate to the frontend directory: ```bash cd frontend ``` 2. Install dependencies: ```bash npm install ``` 3. Run the frontend server: ```bash npm start ``` The frontend will run on `http://localhost:3000` ## Usage 1. Open your browser and go to `http://localhost:3000` 2. Enter your email address to subscribe to the newsletter 3. View the latest Munich news on the homepage 4. The backend will aggregate news from multiple Munich news sources ## Sending Newsletters To send newsletters to all subscribers, you can add a scheduled task or manually trigger the `send_newsletter()` function in `app.py`. For production, consider using: - **Cron jobs** (Linux/Mac) - **Task Scheduler** (Windows) - **Celery** with Redis/RabbitMQ for more advanced scheduling - **Cloud functions** (AWS Lambda, Google Cloud Functions) Example cron job to send daily at 8 AM: ``` 0 8 * * * cd /path/to/munich-news/backend && python -c "from app import send_newsletter; send_newsletter()" ``` ## Project Structure ``` munich-news/ ├── backend/ # Main API server │ ├── app.py # Flask application entry point │ ├── config.py # Configuration management │ ├── database.py # Database connection │ ├── routes/ # API endpoints (blueprints) │ ├── services/ # Business logic │ ├── templates/ # Email templates │ └── requirements.txt # Python dependencies ├── news_crawler/ # Crawler microservice │ ├── crawler_service.py # Standalone crawler │ ├── ollama_client.py # AI summarization client │ ├── requirements.txt # Crawler dependencies │ └── README.md # Crawler documentation ├── news_sender/ # Newsletter sender microservice │ ├── sender_service.py # Standalone email sender │ ├── newsletter_template.html # Email template │ ├── requirements.txt # Sender dependencies │ └── README.md # Sender documentation ├── frontend/ # Web interface │ ├── server.js # Express server │ ├── package.json # Node.js dependencies │ └── public/ │ ├── index.html # Main page │ ├── styles.css # Styling │ └── app.js # Frontend JavaScript ├── docker-compose.yml # Docker Compose for MongoDB (development) ├── docker-compose.prod.yml # Docker Compose with authentication (production) └── README.md ``` ## API Endpoints ### `POST /api/subscribe` Subscribe to the newsletter - Body: `{ "email": "user@example.com" }` ### `POST /api/unsubscribe` Unsubscribe from the newsletter - Body: `{ "email": "user@example.com" }` ### `GET /api/news` Get latest Munich news articles ### `GET /api/stats` Get subscription statistics - Returns: `{ "subscribers": number, "articles": number, "crawled_articles": number }` ### `GET /api/news/` Get full article content by URL - Returns: Full article with content, author, word count, etc. ### `GET /api/ollama/ping` Test connection to Ollama server - Returns: Connection status and Ollama configuration - Response examples: - Success: `{ "status": "success", "message": "...", "response": "...", "ollama_config": {...} }` - Disabled: `{ "status": "disabled", "message": "...", "ollama_config": {...} }` - Error: `{ "status": "error", "message": "...", "error_details": "...", "troubleshooting": {...}, "ollama_config": {...} }` ### `GET /api/ollama/models` List available models on Ollama server - Returns: List of available models and current configuration - Response: `{ "status": "success", "models": [...], "current_model": "...", "ollama_config": {...} }` ### `GET /api/rss-feeds` Get all RSS feeds - Returns: `{ "feeds": [...] }` ### `POST /api/rss-feeds` Add a new RSS feed - Body: `{ "name": "Feed Name", "url": "https://example.com/rss" }` - Returns: `{ "message": "...", "id": "..." }` ### `DELETE /api/rss-feeds/` Remove an RSS feed - Returns: `{ "message": "..." }` ### `PATCH /api/rss-feeds//toggle` Toggle RSS feed active status - Returns: `{ "message": "...", "active": boolean }` ## Database Schema ### Articles Collection ```javascript { _id: ObjectId, title: String, link: String (unique), summary: String, source: String, published_at: String, created_at: DateTime } ``` ### Subscribers Collection ```javascript { _id: ObjectId, email: String (unique, lowercase), subscribed_at: DateTime, status: String ('active' | 'inactive') } ``` **Indexes:** - `articles.link` - Unique index to prevent duplicate articles - `articles.created_at` - For efficient sorting - `subscribers.email` - Unique index for email lookups - `subscribers.subscribed_at` - For analytics ## News Crawler Microservice The project includes a standalone crawler microservice that fetches full article content from RSS feeds. ### Running the Crawler ```bash cd news_crawler # Install dependencies pip install -r requirements.txt # Run crawler python crawler_service.py 10 ``` See `news_crawler/README.md` for detailed documentation. ### What It Does - Crawls full article content from RSS feed links - Extracts text, word count, and metadata - Stores in MongoDB for AI processing - Skips already-crawled articles - Rate-limited (1 second between requests) ## Customization ### Adding News Sources Use the API to add RSS feeds dynamically: ```bash curl -X POST http://localhost:5001/api/rss-feeds \ -H "Content-Type: application/json" \ -d '{"name": "Your Source Name", "url": "https://example.com/rss"}' ``` ### Styling Modify `frontend/public/styles.css` to customize the appearance. ## License MIT ## Contributing Feel free to submit issues and enhancement requests!