Add authentication improvements and search functionality

- Implement anonymous access control with ALLOW_ANONYMOUS_ACCESS env var
- Add complete password reset workflow with token-based validation
- Add username recovery functionality for better UX
- Implement full-text search API with relevance scoring and highlighting
- Add Docker compatibility improvements with permission handling and fallback storage
- Add quick stats API for real-time dashboard updates
- Improve security with proper token expiration and input validation
- Add search result pagination and navigation
- Enhance error handling and logging throughout the application

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-11 21:11:03 -05:00
parent 36bb905f99
commit 83dd85ffa3
8 changed files with 960 additions and 12 deletions

View File

@@ -16,7 +16,10 @@ from data_collection_lib import data_methods
# ===== STORAGE FUNCTIONS =====
def ensure_directories(storage_dir: str) -> Dict[str, Path]:
"""Create and return directory paths"""
"""Create and return directory paths with proper error handling"""
import logging
logger = logging.getLogger(__name__)
base = Path(storage_dir)
dirs = {
@@ -27,7 +30,40 @@ def ensure_directories(storage_dir: str) -> Dict[str, Path]:
}
for path in dirs.values():
path.mkdir(parents=True, exist_ok=True)
try:
path.mkdir(parents=True, exist_ok=True)
# Set proper permissions for Docker compatibility
try:
path.chmod(0o755)
except (OSError, PermissionError):
logger.warning(f"Could not set permissions for directory: {path}")
except PermissionError as e:
logger.error(f"Permission denied creating directory {path}: {e}")
# For Docker compatibility, try using a temporary directory
import tempfile
temp_dir = Path(tempfile.gettempdir()) / 'balanceboard_data'
temp_dir.mkdir(parents=True, exist_ok=True)
temp_dir.chmod(0o755)
logger.info(f"Using temporary directory: {temp_dir}")
# Update paths to use temp directory
dirs['base'] = temp_dir
dirs['posts'] = temp_dir / 'posts'
dirs['comments'] = temp_dir / 'comments'
dirs['moderation'] = temp_dir / 'moderation'
# Create temp directories
for temp_path in dirs.values():
temp_path.mkdir(parents=True, exist_ok=True)
try:
temp_path.chmod(0o755)
except (OSError, PermissionError):
pass # Ignore permission errors on temp files
except OSError as e:
logger.error(f"Error creating directory {path}: {e}")
# Continue with other directories
continue
return dirs
@@ -46,10 +82,40 @@ def load_index(storage_dir: str) -> Dict:
def save_index(index: Dict, storage_dir: str):
"""Save post index to disk"""
"""Save post index to disk with error handling"""
import logging
logger = logging.getLogger(__name__)
index_file = Path(storage_dir) / 'post_index.json'
with open(index_file, 'w') as f:
json.dump(index, f, indent=2)
try:
# Create backup of existing index
if index_file.exists():
backup_file = index_file.with_suffix('.json.backup')
try:
import shutil
shutil.copy2(index_file, backup_file)
except (OSError, PermissionError):
logger.warning(f"Could not create backup of index file: {index_file}")
with open(index_file, 'w') as f:
json.dump(index, f, indent=2)
except PermissionError as e:
logger.error(f"Permission denied saving index to {index_file}: {e}")
# Try to save to temp directory as fallback
try:
import tempfile
temp_dir = Path(tempfile.gettempdir()) / 'balanceboard_data'
temp_index_file = temp_dir / 'post_index.json'
temp_dir.mkdir(parents=True, exist_ok=True)
with open(temp_index_file, 'w') as f:
json.dump(index, f, indent=2)
logger.info(f"Index saved to temporary location: {temp_index_file}")
except Exception as temp_e:
logger.error(f"Failed to save index to temp location: {temp_e}")
except OSError as e:
logger.error(f"Error saving index to {index_file}: {e}")
def load_state(storage_dir: str) -> Dict:
@@ -66,10 +132,29 @@ def load_state(storage_dir: str) -> Dict:
def save_state(state: Dict, storage_dir: str):
"""Save collection state to disk"""
"""Save collection state to disk with error handling"""
import logging
logger = logging.getLogger(__name__)
state_file = Path(storage_dir) / 'collection_state.json'
with open(state_file, 'w') as f:
json.dump(state, f, indent=2)
try:
with open(state_file, 'w') as f:
json.dump(state, f, indent=2)
except PermissionError as e:
logger.error(f"Permission denied saving state to {state_file}: {e}")
# Try to save to temp directory as fallback
try:
import tempfile
temp_dir = Path(tempfile.gettempdir()) / 'balanceboard_data'
temp_state_file = temp_dir / 'collection_state.json'
temp_dir.mkdir(parents=True, exist_ok=True)
with open(temp_state_file, 'w') as f:
json.dump(state, f, indent=2)
logger.info(f"State saved to temporary location: {temp_state_file}")
except Exception as temp_e:
logger.error(f"Failed to save state to temp location: {temp_e}")
except OSError as e:
logger.error(f"Error saving state to {state_file}: {e}")
def generate_uuid() -> str: