mirror of
https://github.com/Such-Software/xmrcheckout.git
synced 2026-10-25 06:08:47 +01:00
merge upstream: system status, health monitoring, connectivity caching
Merges xmrcheckout/xmrcheckout main (7 commits since 655d037):
- Add Monero system status and reconciler heartbeat
- Surface Monero health status in UI (dashboard + invoice pages)
- Harden reconciler status heartbeat startup
- Avoid false Monero outage states in UI
- Cache Monero connectivity in system status
- Next.js security patch (15.5.9 → 15.5.14)
- Bump Monero Docker defaults to 0.18.4.6
Resolved conflicts in reconciler.py (kept our advisory lock + upstream
heartbeat), routes.py (kept both imports), README.md (kept both sections).
This commit is contained in:
+53
-2
@@ -22,12 +22,13 @@ from .config import (
|
||||
FOUNDER_PAYMENT_ADDRESS,
|
||||
FOUNDER_VIEW_KEY,
|
||||
INVOICE_DEFAULT_EXPIRY_HOURS,
|
||||
INVOICE_RECONCILE_INTERVAL_SECONDS,
|
||||
OPEN_REGISTRATION,
|
||||
QR_STORAGE_DIR,
|
||||
)
|
||||
from .db import get_db
|
||||
from .formatting import format_xmr_amount
|
||||
from .models import Invoice, ProfileHistory, User, Webhook, WebhookDelivery
|
||||
from .models import Invoice, ProfileHistory, SystemStatus, User, Webhook, WebhookDelivery
|
||||
from monero.address import Address, IntegratedAddress, SubAddress
|
||||
from .rates import get_xmr_rate
|
||||
from .subaddress_allocator import MAX_SUBADDRESS_INDEX, create_subaddress_for_user
|
||||
@@ -43,6 +44,7 @@ from .schemas import (
|
||||
LoginResponse,
|
||||
ProfileResponse,
|
||||
ProfileUpdate,
|
||||
SystemStatusResponse,
|
||||
WebhookCreate,
|
||||
WebhookDeliveryResponse,
|
||||
WebhookResponse,
|
||||
@@ -59,7 +61,6 @@ from .security import (
|
||||
)
|
||||
from .webhooks import build_webhook_payload, dispatch_webhooks
|
||||
from .qr_codes import ensure_invoice_qr_png, invoice_qr_url, resolve_qr_settings
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
WEBHOOK_EVENTS = (
|
||||
@@ -78,12 +79,57 @@ def _require_donations_enabled() -> None:
|
||||
)
|
||||
|
||||
MAX_QR_LOGO_DATA_URL_LENGTH = 120_000
|
||||
MONERO_CONNECTIVITY_STATUS_NAME = "monero_connectivity"
|
||||
|
||||
|
||||
def _get_user_for_api_key(db: Session, api_key: str) -> User | None:
|
||||
return db.query(User).filter(User.api_key_hash == hash_api_key(api_key)).first()
|
||||
|
||||
|
||||
def _status_response_from_rows(
|
||||
monero_status: SystemStatus | None,
|
||||
reconciler_status: SystemStatus | None,
|
||||
) -> SystemStatusResponse:
|
||||
wallet_rpc = (
|
||||
str(monero_status.wallet_rpc)
|
||||
if monero_status and monero_status.wallet_rpc
|
||||
else "unreachable"
|
||||
)
|
||||
daemon = (
|
||||
str(monero_status.daemon)
|
||||
if monero_status and monero_status.daemon
|
||||
else "unknown"
|
||||
)
|
||||
daemon_height = monero_status.daemon_height if monero_status else None
|
||||
return SystemStatusResponse(
|
||||
wallet_rpc=wallet_rpc,
|
||||
daemon=daemon,
|
||||
daemon_height=daemon_height,
|
||||
invoice_reconcile_interval_seconds=INVOICE_RECONCILE_INTERVAL_SECONDS,
|
||||
last_reconcile_started_at=(
|
||||
reconciler_status.last_reconcile_started_at if reconciler_status else None
|
||||
),
|
||||
last_reconcile_completed_at=(
|
||||
reconciler_status.last_reconcile_completed_at if reconciler_status else None
|
||||
),
|
||||
last_reconcile_error=(
|
||||
reconciler_status.last_reconcile_error if reconciler_status else None
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _load_public_system_status(db: Session) -> SystemStatusResponse:
|
||||
reconciler_status = (
|
||||
db.query(SystemStatus).filter(SystemStatus.name == "reconciler").first()
|
||||
)
|
||||
monero_status = (
|
||||
db.query(SystemStatus)
|
||||
.filter(SystemStatus.name == MONERO_CONNECTIVITY_STATUS_NAME)
|
||||
.first()
|
||||
)
|
||||
return _status_response_from_rows(monero_status, reconciler_status)
|
||||
|
||||
|
||||
def _get_founder_user(db: Session) -> User:
|
||||
if not FOUNDER_PAYMENT_ADDRESS or not FOUNDER_VIEW_KEY:
|
||||
raise HTTPException(
|
||||
@@ -541,6 +587,11 @@ def get_donation_status(
|
||||
return _public_invoice_status_response(db, invoice, request)
|
||||
|
||||
|
||||
@router.get("/api/core/public/system/status", response_model=SystemStatusResponse)
|
||||
def get_public_system_status(db: Session = Depends(get_db)):
|
||||
return _load_public_system_status(db)
|
||||
|
||||
|
||||
@router.post("/api/core/webhooks", response_model=WebhookResponse)
|
||||
def register_webhook(
|
||||
payload: WebhookCreate,
|
||||
|
||||
Reference in New Issue
Block a user