d
2025-10-12 23:40:33,603 - apscheduler.scheduler - INFO - Scheduler started
2025-10-12 23:40:33,605 - polling_service - INFO - Polling scheduler started
2025-10-12 23:40:33,605 - apscheduler.scheduler - INFO - Added job "Check and poll sources" to job store "default"
2025-10-12 23:40:33,606 - polling_service - INFO - Poll checker job scheduled
2025-10-12 23:40:33,610 - filter_pipeline.config - INFO - Loaded filter config from filter_config.json
2025-10-12 23:40:33,610 - filter_pipeline.config - INFO - Loaded 5 filtersets from filtersets.json
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/usr/local/lib/python3.12/site-packages/flask/__main__.py", line 3, in <module>
main()
File "/usr/local/lib/python3.12/site-packages/flask/cli.py", line 1131, in main
cli.main()
File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1383, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1850, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1246, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/click/core.py", line 814, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/click/decorators.py", line 93, in new_func
return ctx.invoke(f, obj, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/click/core.py", line 814, in invoke
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/cli.py", line 979, in run_command
raise e from None
File "/usr/local/lib/python3.12/site-packages/flask/cli.py", line 963, in run_command
app: WSGIApplication = info.load_app() # pyright: ignore
^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/cli.py", line 349, in load_app
app = locate_app(import_name, name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/cli.py", line 245, in locate_app
__import__(module_name)
File "/app/app.py", line 91, in <module>
✓ Database tables created
filter_engine = FilterEngine.get_instance()
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/filter_pipeline/engine.py", line 55, in get_instance
cls._instance = cls()
^^^^^
File "/app/filter_pipeline/engine.py", line 43, in __init__
self.cache = FilterCache(self.config.get_cache_dir())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/filter_pipeline/cache.py", line 28, in __init__
self.cache_dir.mkdir(parents=True, exist_ok=True)
File "/usr/local/lib/python3.12/pathlib.py", line 1311, in mkdir
os.mkdir(self, mode)
PermissionError: [Errno 13] Permission denied: 'data/filter_cach
## Problem Fixed:
The /settings/communities page was throwing a 500 error with:
`NameError: name 'platform_config' is not defined`
## Root Cause:
In the settings_communities() function, I was trying to use platform_config
without loading it first using the load_platform_config() function.
## Solution:
Added platform_config = load_platform_config() before using the variable.
This fixes the 500 error when users try to access community settings.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Problem Fixed:
The admin setup page (admin_setup.html) had incomplete form styling that could appear "corrupted" or broken, especially when form validation errors occurred.
## Root Cause:
The admin_setup.html template was missing explicit form group styles and relying only on base.html auth-form styles, which weren't sufficient for all form states and could lead to layout issues.
## Solution Implemented:
### Enhanced Admin Setup Form Styling
- **Added explicit form-group styles** - Ensures proper spacing and layout
- **Complete auth-form style definitions** - All form elements now have consistent styling
- **Proper focus states** - Form inputs have correct focus indicators
- **Box-sizing fix** - Prevents layout overflow issues
- **Enhanced button styling** - Consistent with other admin pages
- **Form validation support** - Proper styling for error states
### Style Additions:
- Form group margin and spacing
- Input field padding, borders, and backgrounds
- Focus states with proper color transitions
- Button hover effects and animations
- Auth footer styling for better layout
The admin setup page now has robust, consistent styling that matches the rest of the admin interface and won't appear corrupted under various states.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Problems Fixed:
1. **Template inheritance inconsistency** - Some admin pages used standalone HTML while others extended base.html
2. **CSS duplication** - Each admin page had duplicate styles for similar components
3. **Header styling variations** - Different admin pages had slightly different header styles
4. **Inconsistent class naming** - Mixed naming patterns across admin templates
## Root Cause:
Admin pages were developed independently without a shared styling foundation, leading to code duplication and visual inconsistencies.
## Solution Implemented:
### New Shared Admin Base
- **Created `_admin_base.html`** - Unified base template for all admin pages
- **Consolidated styles** - Moved common admin styles to shared base template
- **Standardized components** - Unified buttons, tables, badges, forms, etc.
- **Consistent layout** - Standard admin container, header, and navigation structure
### Refactored Templates
- **`admin.html`** - Now extends `_admin_base.html`, removed 300+ lines of duplicate CSS
- **`admin_polling.html`** - Converted to use base template, cleaner structure
- **`admin_polling_logs.html`** - Completely rewritten to use base template
- **Consistent class names** - All admin tables now use `.admin-table` instead of mixed names
### Benefits
- **Maintainability** - Single source of truth for admin styling
- **Consistency** - All admin pages now have identical look and feel
- **Performance** - Reduced CSS duplication improves load times
- **Extensibility** - Easy to add new admin pages with consistent styling
All admin pages now share a unified, professional appearance\!
Commit: [current]
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Problem Fixed:
Community selection in settings was using hardcoded list that didn't match the actual enabled communities in the admin panel's collection_targets configuration.
## Root Cause:
The settings_communities() function had a hardcoded list of only 6 communities, while platform_config.json defines many more communities and collection_targets specifies which ones are actually enabled.
## Solution:
- **Dynamic community loading** - Reads from platform_config.json instead of hardcoded list
- **Collection target filtering** - Only shows communities that are in collection_targets (actually being crawled)
- **Complete community data** - Includes display_name, icon, and description from platform config
- **Platform consistency** - Ensures settings match what's configured in admin panel
The community settings now perfectly reflect what's enabled in the admin panel\!
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Investigation revealed the filtration system had several issues:
1. **Hardcoded filters**: Sidebar had hardcoded filters that didn't match actual filtersets
2. **No backend integration**: Filter switching didn't pass filter parameters to API
3. **Missing filter endpoint**: No API to get available filters dynamically
Fixes implemented:
- Added /api/filters endpoint to get available filtersets from filter engine
- Updated dashboard to load filters dynamically from backend
- Connected filter switching to actually apply different filtersets
- Added filter override parameter to /api/posts endpoint
- Updated JavaScript to properly handle filter state and switching
- Added proper loading states and error handling
Available filters now show:
- All Content (no_filter)
- Safe Content (safe_content)
- Tech Only (tech_only)
- High Quality (high_quality)
- Custom Example (custom_example)
The filtration system now properly applies the selected filterset to posts
using the existing filter pipeline infrastructure.
Fixes#19
~claude
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added full bookmark functionality for users to save posts:
Backend Features:
- New Bookmark model with user_id, post_uuid, and cached metadata
- API endpoints:
- POST /api/bookmark - Toggle bookmark status
- GET /api/bookmarks - Get user's bookmarks with pagination
- GET /api/bookmark-status/<uuid> - Check if post is bookmarked
- Database migration script for bookmarks table
Frontend Features:
- New /bookmarks page with post list and management
- Bookmark buttons on post cards with save/unsave toggle
- Real-time bookmark status loading and updates
- Navigation menu integration
- Responsive design with archived post handling
UI Components:
- Modern bookmark button with hover states
- Pagination for bookmark listings
- Empty state for users with no bookmarks
- Error handling and loading states
- Remove bookmark functionality
The system handles:
- Unique bookmarks per user/post combination
- Cached post metadata for performance
- Graceful handling of deleted/archived posts
- Authentication requirements for all bookmark features
Users can now save posts for later reading and manage their bookmarks
through a dedicated bookmarks page.
Fixes#20
~claude
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented configurable time-based filtering to show only recent posts:
- Added new experience settings for time filtering:
- time_filter_enabled: Toggle to enable/disable time filtering
- time_filter_days: Number of days to show (1, 3, or 7 days)
Changes:
- Updated settings_experience.html with time filter controls
- Added JavaScript toggle for showing/hiding time filter options
- Modified backend to save and validate new time filter settings
- Updated API posts endpoint to filter posts by timestamp when enabled
- Added time filtering to anonymous user default settings
Users can now limit their feed to show only posts from:
- Last 24 hours
- Last 3 days
- Last week (default)
This addresses the need for a "show only posts from last x time" feature
as a default filtering option.
Fixes#21
~claude
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added environment variables APP_NAME and LOGO_PATH to make the application
branding configurable:
- APP_NAME (default: 'BalanceBoard'): Sets the application name
- LOGO_PATH (default: 'logo.png'): Sets the logo file path
Changes:
- Added configuration variables to app.py
- Updated logo serving route to support custom paths
- Added template context processor to inject APP_NAME
- Updated all templates to use {{ APP_NAME }} instead of hardcoded 'BalanceBoard'
- Updated navigation and branding to use configurable values
Users can now customize their installation by setting:
export APP_NAME="My Custom Board"
export LOGO_PATH="/path/to/my/logo.png"
Fixes#22
~claude
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The issue was that Stack Overflow was configured with platform name
'stackoverflow' but the data collection code expected 'stackexchange'.
Fixed by:
1. Renamed platform from 'stackoverflow' to 'stackexchange' in platform_config.json
2. Added Stack Overflow collection target to enable crawling
3. Updated templates and app.py to use the correct platform name
4. Added default 'stackoverflow' community alongside existing featured/newest
This resolves the platform name mismatch that prevented Stack Overflow
from being crawlable.
Fixes#23
~claude
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The issue was that user community preferences stored in settings weren't
being applied when fetching posts. Added logic to filter posts based on
user's selected communities from their settings.
Fixes#24
~claude
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove duplicate onchange handler from avatar file input
- Prevent potential form submission conflicts between inline JS and event listener
- Avatar upload now properly uses only the JavaScript event listener with validation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add navigation bar to admin setup page (Fixes issue #14)
- Make logo clickable to go to front page in admin setup (Fixes issue #13)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unnecessary wrapper form around Profile Picture section
- Avatar upload form is now standalone, not nested
- Prevents browser validation from requiring username/email when uploading avatar
Fixes#12
- Create reusable _nav.html navigation include
- Add topbar to all settings pages (settings, profile, communities, filters, experience)
- Add topbar to all admin pages (admin, polling, polling_logs, setup)
- Replace hardcoded nav in dashboard with include
- Wrap logo in link to index page (fixes clicking logo to go home)
Fixes#14, #13
- Add check for AUTH0 credentials before attempting login
- Show friendly error message if Auth0 not configured
- Hide Auth0 button on login page when not configured
- Add try/catch for auth0.authorize_redirect() failures
Fixes#5
- Add calculate_quick_stats() to get real-time post counts
- Calculate posts from last 24 hours instead of hardcoded value
- Pass quick_stats to dashboard template
- Update template to display dynamic posts_today count
Fixes#7
- Add build_comment_tree() to organize comments hierarchically
- Create recursive Jinja macro to render nested comments
- Add visual styling with left border and indentation
- Comments now display as threaded tree structure
Fixes#10
Template expects dictionary but route was passing a list, causing:
jinja2.exceptions.UndefinedError: 'list object' has no attribute 'no_filter'
Changed to build dictionary mapping filter names to full configs.
Fixes#9
Complete integration of filter pipeline with web application:
App.py Integration:
- Initialize FilterEngine singleton at startup
- Update /api/posts endpoint to use FilterEngine.apply_filterset()
- Apply user's filterset preference from settings
- Sort posts by filter_score (highest first), then timestamp
- Add filter metadata to post responses (filter_score, categories, tags)
Settings Page Updates:
- Dynamically load available filtersets from FilterEngine
- Show filterset descriptions in settings UI
- Validate filterset selection against FilterEngine
Security:
- Update _is_safe_filterset() to use FilterEngine's list
- Dynamic ALLOWED_FILTERSETS from filtersets.json
User Experience:
- Posts automatically filtered based on user preferences
- Quality/relevance scores affect post ordering
- Transparent filter metadata available in API
Caching:
- FilterEngine uses 3-level cache for efficiency
- Cache reused across page loads (5min TTL)
- AI results cached permanently
Next Steps:
- Polling service integration
- Database model for persistent results
- UI for cache stats and filter debugging
Related to filtering engine implementation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements plugin-based content filtering system with multi-level caching:
Core Components:
- FilterEngine: Main orchestrator for content filtering
- FilterCache: 3-level caching (memory, AI results, filterset results)
- FilterConfig: Configuration loader for filter_config.json & filtersets.json
- FilterResult & AIAnalysisResult: Data models for filter results
Architecture:
- BaseStage: Abstract class for pipeline stages
- BaseFilterPlugin: Abstract class for filter plugins
- Multi-threaded parallel processing support
- Content-hash based AI result caching (cost savings)
- Filterset result caching (fast filterset switching)
Configuration:
- filter_config.json: AI models, caching, parallel workers
- Using only Llama 70B for cost efficiency
- Compatible with existing filtersets.json
Integration:
- apply_filterset() API compatible with user preferences
- process_batch() for batch post processing
- Lazy-loaded stages to avoid import errors when AI disabled
Related to issue #8 (filtering engine implementation)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed BuildError caused by incorrect endpoint name in anonymous mode.
The route is called 'signup' not 'register' in app.py line 878.
Error was:
werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'register'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed redundant current_user.is_authenticated checks in the else block
of the navigation menu. The else block only executes for authenticated
users per app.py logic (line 278 vs 293), so the nested checks were
dead code that created confusion.
Changes:
- Removed defensive checks for unauthenticated users in authenticated block
- Added clarifying comment about when else block executes
- Simplified template logic for better maintainability
- Removed dead code paths (Anonymous User label, ? avatar)
Addresses concerns raised in Issue #2 about confusing template logic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed dashboard.html template error accessing current_user.username for anonymous users
- Added ALLOW_ANONYMOUS_ACCESS environment variable with default true
- Enhanced index route logic to properly check config before allowing anonymous access
- Added proper environment variable to docker-compose.yml
- Anonymous access now works without 500 server errors
Fixes issue #2 completely - anonymous access is now functional
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend changes:
- Added search query parameter (q) to /api/posts endpoint
- Search filters posts by title, content, author, and source
- Case-insensitive search with substring matching
Frontend changes:
- Made search bar functional with Enter key and click support
- Added performSearch() function to trigger searches
- Added Clear Search button that appears during active search
- Search results update feed title to show query
- Integrated search with existing pagination and filtering
- Preserves anonymous/authenticated feed title when clearing search
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added debug logging to post_detail route to track comment loading
- Created migration script for poll source fields (max_posts, fetch_comments, priority)
- Migration adds default values to ensure comments are fetched
The issue may be that existing poll sources in database dont have fetch_comments field.
Migration needs to be run on server with database access.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added LICENSE file with dual licensing:
- Primary license: AGPL-3.0 for open source use
- Commercial licenses available from copyright holder
- AGPL-3.0 licenses are irrevocable (cannot be revoked)
- Commercial licensing rights reserved only for commercial use cases
- Clarifies that non-commercial use is governed by AGPL-3.0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created README.md with complete project documentation:
- Project overview and feature list
- Quick start guide for local development
- Docker deployment instructions
- Configuration details for platforms and polling
- Architecture overview and data flow
- API endpoint documentation
- Project structure and file organization
- Guide for adding new platforms
- Contributing guidelines
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created DEPLOYMENT.md with comprehensive instructions for:
- Making commits with proper formatting
- Commenting on Gitea issues via API
- Database migration procedures
- Docker build and deployment workflow
- Common troubleshooting steps
- Checklist for each commit cycle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added reset_token and reset_token_expiry fields to User model
- Implemented generate_reset_token(), verify_reset_token(), and clear_reset_token() methods
- Created password reset request form (/password-reset-request)
- Created password reset form (/password-reset/<token>)
- Added "Forgot password?" link to login page
- Reset tokens expire after 1 hour for security
- Created migration script to add new database columns
- Reset links are logged (would be emailed in production)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Modified index route to allow browsing without login
- Set default user_settings for anonymous users with safe defaults
- Added anonymous flag to dashboard template
- Updated navigation to show Login/Sign Up buttons for anonymous users
- Changed feed header to "Public Feed" for anonymous browsing
- Hide Customize button for anonymous users
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Edit button for each poll source
- Modal dialog for editing all source settings
- Add max_posts, fetch_comments, priority fields to add form
- Display source settings in source list
- JavaScript modal management for editing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Read max_posts from source.max_posts (fallback to 100)
- Read fetch_comments from source settings
- Allows customizing collection per source
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Polling now always collects posts from last 24 hours
- Removes resume feature that created too-narrow time windows
- Fixes issue where polls returned 0 posts due to minutes-wide ranges
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add 'Custom Source' text input field to polling form
- Allows manual entry of subreddits (r/subreddit) or RSS URLs
- Custom input overrides dropdown selection if filled
- Dropdown becomes optional when custom source is entered
- Backend prioritizes custom_source_id over dropdown source_id
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Change 'Poll Interval (minutes)' to just 'Poll Interval'
- Create data subdirectories with correct permissions on server
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Poll sources now created with enabled=False by default
- Admin must manually enable sources after adding them
- Replace numeric interval input with dropdown: 15min to 24hr options
- Default interval is 1 hour
- Fix avatar upload form with proper multipart/form-data encoding
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement RSS/Atom feed parser using feedparser library
- Add RSS platform configuration with sample feeds (HN RSS, Lobsters RSS, Reddit RSS)
- Support both RSS 2.0 and Atom formats with automatic detection
- Extract and normalize: title, author, link, content, tags, timestamps
- HTML entity unescaping and tag stripping for clean content
- Fallback handling for missing fields
- Users can add any RSS feed URL as a collection source
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Flask-based web application with PostgreSQL
- User authentication and session management
- Content moderation and filtering
- Docker deployment with docker-compose
- Admin interface for content management
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>