Complete ircquotes application with all features

- Added copy quote functionality with clipboard integration
- Implemented bulk moderation actions for admin
- Created mobile responsive design with bash.org styling
- Added API rate limiting per IP address
- Implemented dark mode toggle with flash prevention
- Enhanced error messages throughout application
- Fixed all security vulnerabilities (SQL injection, XSS, CSRF)
- Added comprehensive rate limiting on all endpoints
- Implemented secure session configuration
- Added input validation and length limits
- Created centralized configuration system with config.json
- Set up production deployment with Gunicorn
- Added security headers and production hardening
- Added password generation and config management tools
This commit is contained in:
2025-09-20 19:41:23 +01:00
parent 0b1241714d
commit f409977257
21 changed files with 1936 additions and 304 deletions

104
config_loader.py Normal file
View File

@@ -0,0 +1,104 @@
"""
Configuration loader for ircquotes application.
Loads settings from config.json and provides easy access to configuration values.
"""
import json
import os
from typing import Any, Dict
class Config:
"""Configuration manager for ircquotes application."""
def __init__(self, config_file: str = "config.json"):
"""Initialize configuration from JSON file."""
self.config_file = config_file
self._config = self._load_config()
def _load_config(self) -> Dict[str, Any]:
"""Load configuration from JSON file."""
if not os.path.exists(self.config_file):
raise FileNotFoundError(f"Configuration file {self.config_file} not found")
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
return json.load(f)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in configuration file: {e}")
def get(self, key: str, default: Any = None) -> Any:
"""Get configuration value using dot notation (e.g., 'app.host')."""
keys = key.split('.')
value = self._config
for k in keys:
if isinstance(value, dict) and k in value:
value = value[k]
else:
return default
return value
def get_section(self, section: str) -> Dict[str, Any]:
"""Get entire configuration section."""
return self._config.get(section, {})
def reload(self):
"""Reload configuration from file."""
self._config = self._load_config()
# Convenience properties for commonly used settings
@property
def app_name(self) -> str:
return self.get('app.name', 'ircquotes')
@property
def app_host(self) -> str:
return self.get('app.host', '0.0.0.0')
@property
def app_port(self) -> int:
return self.get('app.port', 5050)
@property
def debug_mode(self) -> bool:
return self.get('app.debug', False)
@property
def database_uri(self) -> str:
return self.get('database.uri', 'sqlite:///quotes.db')
@property
def csrf_enabled(self) -> bool:
return self.get('security.csrf_enabled', True)
@property
def rate_limiting_enabled(self) -> bool:
return self.get('rate_limiting.enabled', True)
@property
def quotes_per_page(self) -> int:
return self.get('quotes.per_page', 25)
@property
def min_quote_length(self) -> int:
return self.get('quotes.min_length', 10)
@property
def max_quote_length(self) -> int:
return self.get('quotes.max_length', 5000)
@property
def admin_username(self) -> str:
return self.get('admin.username', 'admin')
@property
def admin_password_hash(self) -> str:
return self.get('admin.password_hash', '')
@property
def logging_level(self) -> str:
return self.get('logging.level', 'WARNING')
# Global configuration instance
config = Config()