server alive

This commit is contained in:
Tykayn 2025-09-15 23:54:04 +02:00 committed by tykayn
parent 1c17b57d8a
commit fab0e979d5
11 changed files with 353 additions and 28 deletions

View file

@ -7,6 +7,7 @@ import re
import falcon
import psycopg2
import psycopg2.extras
import psycopg2.errors
from oedb.models.event import BaseEvent
from oedb.utils.db import db_connect
from oedb.utils.serialization import dumps
@ -219,7 +220,7 @@ class EventResource(BaseEvent):
event_sort=event_sort, limit=limit)
logger.debug(f"Executing SQL: {sql}")
cur.execute(sql)
resp.body = dumps(self.rows_to_collection(cur.fetchall(), geom_only))
resp.text = dumps(self.rows_to_collection(cur.fetchall(), geom_only))
resp.status = falcon.HTTP_200
logger.success(f"Successfully processed GET request to /event")
else:
@ -229,16 +230,24 @@ class EventResource(BaseEvent):
e = cur.fetchone()
if e is not None:
resp.body = dumps(self.row_to_feature(e))
resp.text = dumps(self.row_to_feature(e))
resp.status = falcon.HTTP_200
logger.success(f"Successfully processed GET request to /event/{id}")
else:
resp.status = falcon.HTTP_404
logger.warning(f"Event not found: {id}")
except psycopg2.errors.InsufficientPrivilege as e:
logger.error(f"Permission denied for database table: {e}")
resp.status = falcon.HTTP_500
resp.text = dumps({
"error": "Database permission error",
"message": "The server does not have permission to access the required database tables. Please contact the administrator to fix this issue.",
"details": str(e)
})
except Exception as e:
logger.error(f"Error processing GET request: {e}")
resp.status = falcon.HTTP_500
resp.body = dumps({"error": str(e)})
resp.text = dumps({"error": str(e)})
finally:
db.close()
@ -260,29 +269,29 @@ class EventResource(BaseEvent):
j = json.loads(body)
except Exception as e:
logger.error(f"Invalid JSON or bad encoding: {e}")
resp.body = 'invalid json or bad encoding'
resp.text = 'invalid json or bad encoding'
resp.status = falcon.HTTP_400
return
resp.body = ''
resp.text = ''
if "properties" not in j:
resp.body = resp.body + "missing 'properties' elements\n"
resp.text = resp.text + "missing 'properties' elements\n"
j['properties'] = dict()
if "geometry" not in j:
resp.body = resp.body + "missing 'geometry' elements\n"
resp.text = resp.text + "missing 'geometry' elements\n"
j['geometry'] = None
if "when" not in j['properties'] and ("start" not in j['properties'] or "stop" not in j['properties']):
resp.body = resp.body + "missing 'when' or 'start/stop' in properties\n"
resp.text = resp.text + "missing 'when' or 'start/stop' in properties\n"
j['properties']['when'] = None
if "type" not in j['properties']:
resp.body = resp.body + "missing 'type' of event in properties\n"
resp.text = resp.text + "missing 'type' of event in properties\n"
j['properties']['type'] = None
if "what" not in j['properties']:
resp.body = resp.body + "missing 'what' in properties\n"
resp.text = resp.text + "missing 'what' in properties\n"
j['properties']['what'] = None
if "type" in j and j['type'] != 'Feature':
resp.body = resp.body + 'geojson must be "type":"Feature" only\n'
if id is None and resp.body != '':
resp.text = resp.text + 'geojson must be "type":"Feature" only\n'
if id is None and resp.text != '':
resp.status = falcon.HTTP_400
resp.set_header('Content-type', 'text/plain')
return
@ -319,7 +328,7 @@ class EventResource(BaseEvent):
geometry = dumps(j['geometry'])
h = self.maybe_insert_geometry(geometry, cur)
if len(h) > 1 and h[1] is False:
resp.body = "invalid geometry: %s\n" % h[2]
resp.text = "invalid geometry: %s\n" % h[2]
resp.status = falcon.HTTP_400
resp.set_header('Content-type', 'text/plain')
return
@ -361,11 +370,11 @@ class EventResource(BaseEvent):
AND e.events_when=tstzrange(coalesce(%s, lower(s.events_when)),coalesce(%s,upper(s.events_when)),%s) AND e.events_geo=coalesce(%s, s.events_geo);""",
(id, j['properties']['what'], event_start, event_stop, bounds, h[0]))
dupe = cur.fetchone()
resp.body = """{"duplicate":"%s"}""" % (dupe[0])
resp.text = """{"duplicate":"%s"}""" % (dupe[0])
resp.status = '409 Conflict with event %s' % dupe[0]
logger.warning(f"Duplicate event: {dupe[0]}")
else:
resp.body = """{"id":"%s"}""" % (e[0])
resp.text = """{"id":"%s"}""" % (e[0])
if id is None:
resp.status = falcon.HTTP_201
logger.success(f"Event created with ID: {e[0]}")
@ -455,7 +464,7 @@ class EventResource(BaseEvent):
except Exception as e:
logger.error(f"Error deleting event {id}: {e}")
resp.status = falcon.HTTP_500
resp.body = dumps({"error": str(e)})
resp.text = dumps({"error": str(e)})
db.rollback()
finally:
cur.close()

49
oedb/resources/root.py Normal file
View file

@ -0,0 +1,49 @@
"""
Root resource for the OpenEventDatabase.
"""
import falcon
from oedb.utils.serialization import dumps
from oedb.utils.logging import logger
class RootResource:
"""
Resource for the root endpoint.
Handles the / endpoint.
"""
def on_get(self, req, resp):
"""
Handle GET requests to the / endpoint.
Returns a JSON response with available routes and a welcome message.
Args:
req: The request object.
resp: The response object.
"""
logger.info("Processing GET request to /")
try:
# Create a response with available routes and a welcome message
response = {
"message": "Welcome to the OpenEventDatabase API!",
"available_routes": {
"/": "This endpoint - provides information about available routes",
"/event": "Get events matching specified criteria",
"/event/{id}": "Get a specific event by ID",
"/event/search": "Search for events using a GeoJSON geometry",
"/stats": "Get statistics about the database"
}
}
# Set the response body and status
resp.text = dumps(response)
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /")
except Exception as e:
logger.error(f"Error processing GET request to /: {e}")
resp.status = falcon.HTTP_500
resp.text = dumps({"error": str(e)})
# Create a global instance of RootResource
root = RootResource()

View file

@ -33,7 +33,7 @@ class EventSearch(BaseEvent):
if 'geometry' not in j:
logger.warning("Request body missing 'geometry' field")
resp.status = falcon.HTTP_400
resp.body = json.dumps({"error": "Request body must contain a 'geometry' field"})
resp.text = json.dumps({"error": "Request body must contain a 'geometry' field"})
return
# Pass the query with the geometry to event.on_get
@ -44,8 +44,8 @@ class EventSearch(BaseEvent):
except json.JSONDecodeError as e:
logger.error(f"Error decoding JSON: {e}")
resp.status = falcon.HTTP_400
resp.body = json.dumps({"error": "Invalid JSON in request body"})
resp.text = json.dumps({"error": "Invalid JSON in request body"})
except Exception as e:
logger.error(f"Error processing POST request to /event/search: {e}")
resp.status = falcon.HTTP_500
resp.body = json.dumps({"error": str(e)})
resp.text = json.dumps({"error": str(e)})

View file

@ -45,13 +45,13 @@ class StatsResource:
cur.execute("SELECT row_to_json(stat) from (SELECT events_what as what, left(max(upper(events_when))::text,19) as last, count(*) as count, array_agg(distinct(regexp_replace(regexp_replace(events_tags ->> 'source','^(http://|https://)',''),'/.*',''))) as source from (select * from events order by lastupdate desc limit 10000) as last group by 1 order by 2 desc) as stat;")
recent = cur.fetchall()
resp.body = dumps(dict(events_count=count, last_updated=last, uptime=uptime, db_uptime=pg_uptime, recent=recent))
resp.text = dumps(dict(events_count=count, last_updated=last, uptime=uptime, db_uptime=pg_uptime, recent=recent))
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /stats")
except Exception as e:
logger.error(f"Error processing GET request to /stats: {e}")
resp.status = falcon.HTTP_500
resp.body = dumps({"error": str(e)})
resp.text = dumps({"error": str(e)})
finally:
cur.close()
db.close()