add endpoint for mongo results (basic auth)
This commit is contained in:
152
app.py
152
app.py
@@ -2,9 +2,12 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
from flask import Flask, request, jsonify
|
from functools import wraps
|
||||||
|
from flask import Flask, request, jsonify, Response
|
||||||
from ingress import IngressAPI
|
from ingress import IngressAPI
|
||||||
from models import EventType, Plext
|
from models import EventType, Plext
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from pymongo.errors import PyMongoError
|
||||||
|
|
||||||
# Timezone configuration
|
# Timezone configuration
|
||||||
TIMEZONE = ZoneInfo("Europe/Rome")
|
TIMEZONE = ZoneInfo("Europe/Rome")
|
||||||
@@ -18,6 +21,51 @@ logger = logging.getLogger(__name__)
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def check_basic_auth(username: str, password: str) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the provided username and password match the configured credentials.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username: Username from the request
|
||||||
|
password: Password from the request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if credentials match, False otherwise
|
||||||
|
"""
|
||||||
|
expected_username = os.getenv("BASIC_AUTH_USER")
|
||||||
|
expected_password = os.getenv("BASIC_AUTH_PASSWORD")
|
||||||
|
|
||||||
|
if not expected_username or not expected_password:
|
||||||
|
logger.warning("BASIC_AUTH_USER or BASIC_AUTH_PASSWORD not configured")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return username == expected_username and password == expected_password
|
||||||
|
|
||||||
|
|
||||||
|
def basic_auth_required(f):
|
||||||
|
"""
|
||||||
|
Decorator to require basic authentication for an endpoint.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
401 Unauthorized if authentication fails or is missing
|
||||||
|
"""
|
||||||
|
@wraps(f)
|
||||||
|
def decorated(*args, **kwargs):
|
||||||
|
auth = request.authorization
|
||||||
|
|
||||||
|
if not auth or not check_basic_auth(auth.username, auth.password):
|
||||||
|
return Response(
|
||||||
|
"Could not verify your access level for that URL.\n"
|
||||||
|
"You have to login with proper credentials",
|
||||||
|
401,
|
||||||
|
{"WWW-Authenticate": 'Basic realm="Login Required"'}
|
||||||
|
)
|
||||||
|
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
def parse_timestamp(value: str) -> int:
|
def parse_timestamp(value: str) -> int:
|
||||||
"""
|
"""
|
||||||
Parse timestamp from either milliseconds (int) or ISO 8601 string.
|
Parse timestamp from either milliseconds (int) or ISO 8601 string.
|
||||||
@@ -102,9 +150,93 @@ def plext_to_dict(plext: Plext) -> dict:
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@app.route("/plexts/from-db", methods=["GET"])
|
||||||
|
@basic_auth_required
|
||||||
|
def get_plexts_from_db():
|
||||||
|
"""
|
||||||
|
Get plexts from MongoDB with optional filters.
|
||||||
|
|
||||||
@app.route("/plexts", methods=["GET"])
|
Query Parameters:
|
||||||
def get_plexts():
|
player_name: Filter by player name (optional)
|
||||||
|
timestamp_from: Minimum timestamp in milliseconds (optional)
|
||||||
|
timestamp_to: Maximum timestamp in milliseconds (optional)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response with list of plexts (without _id field)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Parse query parameters
|
||||||
|
player_name = request.args.get("player_name")
|
||||||
|
timestamp_from = request.args.get("timestamp_from")
|
||||||
|
timestamp_to = request.args.get("timestamp_to")
|
||||||
|
|
||||||
|
# Validate and convert timestamp parameters to integers if provided
|
||||||
|
if timestamp_from is not None:
|
||||||
|
try:
|
||||||
|
timestamp_from = int(timestamp_from)
|
||||||
|
except ValueError:
|
||||||
|
return jsonify({"error": "timestamp_from must be an integer"}), 400
|
||||||
|
|
||||||
|
if timestamp_to is not None:
|
||||||
|
try:
|
||||||
|
timestamp_to = int(timestamp_to)
|
||||||
|
except ValueError:
|
||||||
|
return jsonify({"error": "timestamp_to must be an integer"}), 400
|
||||||
|
|
||||||
|
# Build MongoDB filter query
|
||||||
|
filter_query = {}
|
||||||
|
|
||||||
|
if player_name:
|
||||||
|
filter_query["player_name"] = player_name
|
||||||
|
|
||||||
|
if timestamp_from is not None:
|
||||||
|
filter_query["timestamp"] = filter_query.get("timestamp", {})
|
||||||
|
filter_query["timestamp"]["$gte"] = timestamp_from
|
||||||
|
|
||||||
|
if timestamp_to is not None:
|
||||||
|
filter_query["timestamp"] = filter_query.get("timestamp", {})
|
||||||
|
filter_query["timestamp"]["$lte"] = timestamp_to
|
||||||
|
|
||||||
|
# Connect to MongoDB
|
||||||
|
mongo_uri = os.getenv("MONGO_URI")
|
||||||
|
db_name = os.getenv("DB_NAME")
|
||||||
|
collection_name = os.getenv("COLLECTION_NAME")
|
||||||
|
|
||||||
|
client = MongoClient(mongo_uri)
|
||||||
|
db = client[db_name]
|
||||||
|
collection = db[collection_name]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Projection to exclude _id field
|
||||||
|
projection = {"_id": 0}
|
||||||
|
|
||||||
|
# Execute query with sorting (timestamp DESC - most recent first)
|
||||||
|
cursor = collection.find(
|
||||||
|
filter=filter_query,
|
||||||
|
projection=projection
|
||||||
|
).sort("timestamp", -1)
|
||||||
|
|
||||||
|
# Convert cursor to list
|
||||||
|
plexts = list(cursor)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"count": len(plexts),
|
||||||
|
"plexts": plexts
|
||||||
|
})
|
||||||
|
|
||||||
|
except PyMongoError as e:
|
||||||
|
logger.error(f"MongoDB error: {e}")
|
||||||
|
return jsonify({"error": "Database error"}), 500
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("Unexpected error in get_plexts_from_db")
|
||||||
|
return jsonify({"error": "An error occurred"}), 500
|
||||||
|
|
||||||
|
@app.route("/plexts/from-api", methods=["GET"])
|
||||||
|
@basic_auth_required
|
||||||
|
def get_plexts_from_api():
|
||||||
"""
|
"""
|
||||||
Get plexts from the Ingress API.
|
Get plexts from the Ingress API.
|
||||||
|
|
||||||
@@ -199,7 +331,7 @@ def index():
|
|||||||
"name": "Ingress Intel API",
|
"name": "Ingress Intel API",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"/plexts": {
|
"/plexts/from-api": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"description": "Get plexts from the Ingress API",
|
"description": "Get plexts from the Ingress API",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
@@ -213,7 +345,17 @@ def index():
|
|||||||
"max_timestamp": "Maximum timestamp (milliseconds or ISO 8601 format, default: -1)",
|
"max_timestamp": "Maximum timestamp (milliseconds or ISO 8601 format, default: -1)",
|
||||||
},
|
},
|
||||||
"event_types": [e.name for e in EventType],
|
"event_types": [e.name for e in EventType],
|
||||||
}
|
},
|
||||||
|
"/plexts/from-db": {
|
||||||
|
"method": "GET",
|
||||||
|
"description": "Get plexts from MongoDB with optional filters",
|
||||||
|
"parameters": {
|
||||||
|
"player_name": "Filter by player name (optional)",
|
||||||
|
"timestamp_from": "Minimum timestamp in milliseconds (optional)",
|
||||||
|
"timestamp_to": "Maximum timestamp in milliseconds (optional)",
|
||||||
|
},
|
||||||
|
"authentication": "Basic Auth required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user