""" REST API for FactsDB service Provides endpoints for querying facts and managing the system """ from flask import Flask, jsonify, request, Response from functools import wraps from typing import Dict, Any, List import json import os from .config import Config from .database import DatabaseManager from .monitoring import get_metrics, get_metrics_json, start_uptime_monitor, increment_fact_extraction, increment_file_processing, increment_error # Start uptime monitoring uptime_thread = start_uptime_monitor() def require_api_key(f): """Decorator to require API key authentication""" @wraps(f) def decorated_function(*args, **kwargs): api_key = os.getenv('API_KEY', '') if api_key: provided_key = request.headers.get('X-API-Key', '') if provided_key != api_key: return jsonify({'error': 'Unauthorized'}), 401 return f(*args, **kwargs) return decorated_function def create_app() -> Flask: """Create and configure the Flask application""" app = Flask(__name__) config = Config() db_manager = DatabaseManager(config.database) @app.route('/health', methods=['GET']) def health(): """Health check endpoint""" return jsonify({ 'status': 'healthy', 'service': 'FactsDB' }) @app.route('/tables', methods=['GET']) @require_api_key def get_tables(): """Get all available tables with record counts""" try: tables = db_manager.get_all_tables() return jsonify({ 'tables': tables, 'total_tables': len(tables) }) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/tables/', methods=['GET']) @require_api_key def get_table_data(table_name: str): """Get all facts from a specific table""" try: facts = db_manager.query_table(table_name) return jsonify({ 'table_name': table_name, 'facts': facts, 'count': len(facts) }) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/tables//query', methods=['POST']) @require_api_key def query_table(table_name: str): """Query facts from a specific table with custom query""" try: data = request.get_json() query = data.get('query', '') if data else '' facts = db_manager.query_table(table_name, query) return jsonify({ 'table_name': table_name, 'facts': facts, 'count': len(facts) }) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/tables//count', methods=['GET']) @require_api_key def get_table_count(table_name: str): """Get record count for a specific table""" try: count = db_manager.get_table_record_count(table_name) return jsonify({ 'table_name': table_name, 'count': count }) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/fact/', methods=['GET']) @require_api_key def get_fact(fact_id: int): """Get a specific fact by ID""" try: with db_manager.get_connection() as conn: cursor = conn.execute(''' SELECT * FROM facts WHERE id = ? ''', (fact_id,)) fact = cursor.fetchone() if fact: return jsonify(dict(fact)) else: return jsonify({'error': 'Fact not found'}), 404 except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/search', methods=['GET']) @require_api_key def search(): """Search across all tables""" try: query = request.args.get('q', '') if not query: return jsonify({'error': 'Search query required'}), 400 # Simple search implementation - in a real system this would be more sophisticated results = [] # Get all tables tables = db_manager.get_all_tables() for table in tables: table_name = table['table_name'] facts = db_manager.query_table(table_name) for fact in facts: # Search in key fields search_content = f"{fact.get('title', '')} {fact.get('summary', '')} {fact.get('main_topic', '')}" if query.lower() in search_content.lower(): results.append({ 'table_name': table_name, 'fact': fact }) return jsonify({ 'query': query, 'results': results, 'count': len(results) }) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/version', methods=['GET']) def version(): """Get service version""" return jsonify({ 'version': '1.0.0', 'service': 'FactsDB' }) @app.route('/metrics', methods=['GET']) def metrics(): """Prometheus metrics endpoint""" try: metrics_text = get_metrics() return Response(metrics_text, mimetype='text/plain') except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/metrics/json', methods=['GET']) def metrics_json(): """JSON metrics endpoint""" try: metrics_data = get_metrics_json() return jsonify(metrics_data) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 @app.route('/stats', methods=['GET']) @require_api_key def stats(): """Get detailed service statistics""" try: db_stats = db_manager.get_database_stats() metrics_data = get_metrics_json() return jsonify({ 'database_stats': db_stats, 'metrics': metrics_data['metrics'], 'timestamp': metrics_data['timestamp'] }) except Exception as e: increment_error() return jsonify({'error': str(e)}), 500 return app # Create a default app instance app = create_app() if __name__ == '__main__': app.run(debug=False, host='0.0.0.0', port=5000)