Implements the parallel.md workstreams (Agents A-E) toward REFACTOR_GOAL.md. Phase 1 — app factory + blueprints + /api/v1: - app.py -> create_app() factory (no module-level app); entrypoints updated - routes/ (auth, pages, settings, admin, assets) + blueprints/api.py at /api/v1 - config.py / extensions.py / security.py extracted; services/ layer added - endpoint names preserved so template url_for() calls keep resolving (static check: all 27 template url_for endpoints are defined routes) Phase 2 — one pluggable filter system: - filter_pipeline/registry.py: @register_stage / @register_plugin + discover_modules - engine._init_stages() instantiates registered stages (no hardcoded dict); process_batch is AI-aware: only short-circuits to the AI-disabled path when a filterset's stages declare requires_ai, so offline filtersets run with AI off - BaseFilterPlugin gets a consumer (stages/plugins.py); Keyword/Quality re-enabled via filter_config.json plugins config - comment tree modes ported to stages/comment_filter.py + shared rules.py; wired into /api/v1/posts/<uuid> and /api/v1/comments/<uuid> via FilterEngine.filter_comments() (fails open) - offline quality_filter filterset exercises plugins+ranker without AI - legacy filter_lib / comment_lib / html_generation_lib / generate_html / active_html path deleted Phase 3 prep — pluggable fetchers + Postgres models: - Post / Comment SQLAlchemy models added to models.py - migrate_content_to_db.py backfill (idempotent by uuid, batched, --dry-run) - platforms/ fetcher registry (extension point) - live reads/writes still go through PostService (disk JSON); cutover deferred Phase 6 — test harness: - pytest.ini + tests/ (conftest with in-memory SQLite fixture, no Postgres; stubbed polling/filter singletons) - test_app_factory.py (route registration, no module-level app), test_api_contracts.py (posts/post_detail/comments/filters shape with monkeypatched post_service + get_filter_engine), test_filter_pipeline.py + test_plugin_contract.py (Flask-free; validated locally 12/12 incl. drop-in stage/plugin discovered with zero core edits) Other: .gitignore added (__pycache__, data/, secrets); pytest in requirements. Verification: py_compile clean across the project; the Flask-free filter-pipeline and plugin-contract tests pass locally. App-factory / API-contract tests need deps+docker to run; runtime flask routes / Auth0-repeated-create_app also gated on docker. Co-Authored-By: Claude <noreply@anthropic.com>
6.8 KiB
BalanceBoard
A Reddit-style content aggregator that collects posts from multiple platforms (Reddit, Hacker News, RSS feeds) and presents them in a unified, customizable feed.
Features
- Multi-Platform Support: Collect content from Reddit, Hacker News, and RSS feeds
- Automated Polling: Background service polls sources at configurable intervals
- User Authentication: Local accounts with bcrypt password hashing and Auth0 OAuth support
- Anonymous Browsing: Browse public feed without creating an account
- Password Reset: Secure token-based password reset mechanism
- Customizable Feeds: Filter and customize content based on your preferences
- Admin Panel: Manage polling sources, view logs, and configure the system
- Modern UI: Card-based interface with clean, responsive design
Quick Start
Prerequisites
- Python 3.12+
- PostgreSQL database
- Docker (for containerized deployment)
Local Development
-
Clone the repository
git clone https://git.scorpi.us/chelsea/balanceboard.git cd balanceboard -
Set up environment
python3 -m venv venv source venv/bin/activate pip install -r requirements.txt -
Configure environment variables
cp .env.example .env # Edit .env with your database credentials and settings -
Initialize the database
python3 -c "from app import create_app; from database import db; app = create_app(); app.app_context().push(); db.create_all()" -
Run migrations (if needed)
python3 migrate_password_reset.py -
Start the application
python3 app.py -
Access the application
- Open browser to
http://localhost:5000 - Create an account or browse anonymously
- Open browser to
Docker Deployment
-
Build the image
docker build -t git.scorpi.us/chelsea/balanceboard:latest . -
Push to registry
docker push git.scorpi.us/chelsea/balanceboard:latest -
Deploy with docker-compose
docker-compose up -d
See DEPLOYMENT.md for detailed deployment instructions.
Configuration
Platform Sources
Configure available platforms and communities in platform_config.json:
{
"reddit": {
"name": "Reddit",
"communities": [
{
"id": "programming",
"name": "r/programming",
"description": "Computer programming"
}
]
}
}
Polling Configuration
Admins can configure polling sources via the Admin Panel:
- Platform: reddit, hackernews, or rss
- Source ID: Subreddit name, or RSS feed URL
- Poll Interval: How often to check for new content (in minutes)
- Max Posts: Maximum posts to collect per poll
- Fetch Comments: Whether to collect comments
- Priority: low, medium, or high
Environment Variables
Key environment variables (see .env.example):
DATABASE_URL: PostgreSQL connection stringSECRET_KEY: Flask secret key for sessionsAUTH0_DOMAIN: Auth0 domain (if using OAuth)AUTH0_CLIENT_ID: Auth0 client IDAUTH0_CLIENT_SECRET: Auth0 client secret
Architecture
Components
- Flask Web Server (
app.py): Main application server - Polling Service (
polling_service.py): Background scheduler for data collection - Data Collection (
data_collection.py,data_collection_lib.py): Platform-specific data fetchers - Database Models (
models.py): SQLAlchemy ORM models - User Service (
user_service.py): User authentication and management
Database Schema
- users: User accounts with authentication
- poll_sources: Configured polling sources
- poll_logs: History of polling activities
- user_sessions: Active user sessions
Data Flow
- Polling service checks enabled sources at configured intervals
- Data collection fetchers retrieve posts from platforms
- Posts are normalized to a common schema and stored in
data/posts/ - Web interface displays posts from the feed
- Users can filter, customize, and interact with content
API Endpoints
Public Routes
GET /: Main feed (anonymous or authenticated)GET /login: Login pagePOST /login: Authenticate userGET /register: Registration pagePOST /register: Create new accountGET /password-reset-request: Request password resetPOST /password-reset-request: Send reset linkGET /password-reset/<token>: Reset password formPOST /password-reset/<token>: Update password
Authenticated Routes
GET /settings: User settingsGET /logout: Log out
Admin Routes
GET /admin: Admin panelGET /admin/polling: Manage polling sourcesPOST /admin/polling/add: Add new sourcePOST /admin/polling/update: Update source settingsPOST /admin/polling/poll: Manually trigger poll
Development
Project Structure
balanceboard/
├── app.py # Main Flask application
├── polling_service.py # Background polling service
├── data_collection.py # Data collection orchestration
├── data_collection_lib.py # Platform-specific fetchers
├── models.py # Database models
├── user_service.py # User management
├── database.py # Database setup
├── platform_config.json # Platform configurations
├── filtersets.json # Content filter definitions
├── templates/ # Jinja2 templates
├── static/ # Static assets (CSS, JS)
├── themes/ # UI themes
├── data/ # Data storage
│ ├── posts/ # Collected posts
│ ├── comments/ # Collected comments
│ └── moderation/ # Moderation data
├── requirements.txt # Python dependencies
├── Dockerfile # Docker image definition
├── docker-compose.yml # Docker composition
├── README.md # This file
└── DEPLOYMENT.md # Deployment instructions
Adding a New Platform
- Add platform config to
platform_config.json - Implement fetcher in
data_collection_lib.py:fetchers.getPlatformData()converters.platform_to_schema()builders.build_platform_url()
- Update routing in
getData()function - Test data collection
- Add to available sources in admin panel
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
See DEPLOYMENT.md for commit and issue management guidelines.
License
This is a personal project by Chelsea. All rights reserved.
Support
For issues and feature requests, please use the issue tracker at: https://git.scorpi.us/chelsea/balanceboard/issues