sageapi/sageapi/api/health.py
Hermes Agent 40c480e488 fix: sageapi local deployment and test server
- Fix health check sqlExe calls: add missing ns parameter
- Fix accounting ID generation: use getID() instead of uuid4 (VARCHAR length)
- Fix accounting request_id: normalize empty to NULL to avoid UNIQUE constraint violation
- Fix test_server balance/update route: was incorrectly pointing to accounting handler
- Add test_server.py: standalone aiohttp test server for local development
- Update conf/config.yaml: local MySQL credentials (test/test)
- Update db/schema.sql and scripts/generate_ddl.py for local testing
- Fix router sync_status sqlExe call: add missing ns parameter
- Fix sync uapi_sync: use correct table/column names
2026-05-20 22:46:05 +08:00

110 lines
3.3 KiB
Python

"""Health check API handler.
Provides endpoints for service health and readiness checks.
"""
from __future__ import annotations
import json
import time
from typing import Any
from appPublic.log import debug, error
from sqlor.dbpools import DBPools
from ahserver.serverenv import ServerEnv
_START_TIME = time.time()
async def health_check() -> str:
"""Basic health check - returns service status."""
uptime = time.time() - _START_TIME
result = {
'status': 'ok',
'service': 'sageapi',
'uptime_seconds': round(uptime, 1),
'timestamp': time.strftime('%Y-%m-%dT%H:%M:%S%z'),
}
return json.dumps(result, ensure_ascii=False, default=str)
async def readiness_check() -> str:
"""Readiness check - verifies database connectivity."""
result: dict[str, Any] = {
'status': 'unknown',
'checks': {},
}
# Check cache database connection
try:
env = ServerEnv()
dbname = env.get_module_dbname('sageapi')
if not dbname:
result['checks']['cache_db'] = {
'status': 'fail',
'error': 'No database configured for sageapi module',
}
else:
async with DBPools().sqlorContext(dbname) as sor:
rows = await sor.sqlExe('SELECT 1 as ping', {})
result['checks']['cache_db'] = {
'status': 'ok',
'dbname': dbname,
}
except Exception as e:
error(f'readiness_check cache_db error: {e}')
result['checks']['cache_db'] = {
'status': 'fail',
'error': str(e),
}
# Check Sage database connection
try:
from sqlor.dbpools import get_sor_context
async with get_sor_context(env, 'sage') as sor:
rows = await sor.sqlExe('SELECT 1 as ping', {})
result['checks']['sage_db'] = {'status': 'ok'}
except Exception as e:
error(f'readiness_check sage_db error: {e}')
result['checks']['sage_db'] = {
'status': 'fail',
'error': str(e),
}
# Check sync state
try:
async with DBPools().sqlorContext(dbname) as sor:
sql = """
SELECT entity_type, sync_status, last_sync_time
FROM sync_state
ORDER BY last_sync_time DESC
"""
rows = await sor.sqlExe(sql, {})
if isinstance(rows, list):
result['checks']['sync_status'] = {
'status': 'ok',
'entities': [
{
'entity_type': r.get('entity_type', ''),
'sync_status': r.get('sync_status', ''),
'last_sync_time': str(r.get('last_sync_time', '')),
}
for r in rows
],
}
except Exception as e:
result['checks']['sync_status'] = {
'status': 'fail',
'error': str(e),
}
# Overall status
all_ok = all(
check.get('status') == 'ok'
for check in result['checks'].values()
)
result['status'] = 'ready' if all_ok else 'degraded'
return json.dumps(result, ensure_ascii=False, default=str)