Add service information feature with version checks
All checks were successful
PostgreSQL Compatibility Matrix / PG14 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG15 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG16 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG17 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG18 smoke (push) Successful in 8s
All checks were successful
PostgreSQL Compatibility Matrix / PG14 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG15 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG16 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG17 smoke (push) Successful in 8s
PostgreSQL Compatibility Matrix / PG18 smoke (push) Successful in 8s
This commit introduces a new "Service Information" section displaying runtime details, installed version, and update status for the NexaPG application. It includes backend API endpoints, database schema changes, and a corresponding frontend page that allows users to check for updates against the official repository. The `.env` example now includes an `APP_VERSION` variable, and related documentation has been updated.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from fastapi import APIRouter
|
||||
from app.api.routes import admin_settings, admin_users, alerts, auth, health, me, targets
|
||||
from app.api.routes import admin_settings, admin_users, alerts, auth, health, me, service_info, targets
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(health.router, tags=["health"])
|
||||
@@ -7,5 +7,6 @@ api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
|
||||
api_router.include_router(me.router, tags=["auth"])
|
||||
api_router.include_router(targets.router, prefix="/targets", tags=["targets"])
|
||||
api_router.include_router(alerts.router, prefix="/alerts", tags=["alerts"])
|
||||
api_router.include_router(service_info.router, prefix="/service", tags=["service"])
|
||||
api_router.include_router(admin_users.router, prefix="/admin/users", tags=["admin"])
|
||||
api_router.include_router(admin_settings.router, prefix="/admin/settings", tags=["admin"])
|
||||
|
||||
105
backend/app/api/routes/service_info.py
Normal file
105
backend/app/api/routes/service_info.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import os
|
||||
import platform
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.config import get_settings
|
||||
from app.core.db import get_db
|
||||
from app.core.deps import get_current_user
|
||||
from app.models.models import ServiceInfoSettings, User
|
||||
from app.schemas.service_info import ServiceInfoCheckResult, ServiceInfoOut
|
||||
from app.services.audit import write_audit_log
|
||||
from app.services.service_info import (
|
||||
UPSTREAM_REPO_WEB,
|
||||
fetch_latest_from_upstream,
|
||||
is_update_available,
|
||||
utcnow,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
settings = get_settings()
|
||||
service_started_at = datetime.now(timezone.utc)
|
||||
|
||||
|
||||
async def _get_or_create_service_settings(db: AsyncSession) -> ServiceInfoSettings:
|
||||
row = await db.scalar(select(ServiceInfoSettings).limit(1))
|
||||
if row:
|
||||
return row
|
||||
row = ServiceInfoSettings(current_version=settings.app_version)
|
||||
db.add(row)
|
||||
await db.commit()
|
||||
await db.refresh(row)
|
||||
return row
|
||||
|
||||
|
||||
def _to_out(row: ServiceInfoSettings) -> ServiceInfoOut:
|
||||
uptime_seconds = int((utcnow() - service_started_at).total_seconds())
|
||||
return ServiceInfoOut(
|
||||
app_name=settings.app_name,
|
||||
environment=settings.environment,
|
||||
api_prefix=settings.api_v1_prefix,
|
||||
app_version=settings.app_version,
|
||||
hostname=platform.node() or os.getenv("HOSTNAME", "unknown"),
|
||||
python_version=platform.python_version(),
|
||||
platform=platform.platform(),
|
||||
service_started_at=service_started_at,
|
||||
uptime_seconds=max(uptime_seconds, 0),
|
||||
update_source=UPSTREAM_REPO_WEB,
|
||||
latest_version=row.latest_version,
|
||||
latest_ref=(row.release_check_url or None),
|
||||
update_available=row.update_available,
|
||||
last_checked_at=row.last_checked_at,
|
||||
last_check_error=row.last_check_error,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/info", response_model=ServiceInfoOut)
|
||||
async def get_service_info(
|
||||
user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> ServiceInfoOut:
|
||||
_ = user
|
||||
row = await _get_or_create_service_settings(db)
|
||||
return _to_out(row)
|
||||
|
||||
|
||||
@router.post("/info/check", response_model=ServiceInfoCheckResult)
|
||||
async def check_service_version(
|
||||
user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> ServiceInfoCheckResult:
|
||||
row = await _get_or_create_service_settings(db)
|
||||
check_time = utcnow()
|
||||
latest, latest_ref, error = await fetch_latest_from_upstream()
|
||||
|
||||
row.last_checked_at = check_time
|
||||
row.last_check_error = error
|
||||
if latest:
|
||||
row.latest_version = latest
|
||||
row.release_check_url = latest_ref
|
||||
row.update_available = is_update_available(settings.app_version, latest)
|
||||
else:
|
||||
row.update_available = False
|
||||
await db.commit()
|
||||
await db.refresh(row)
|
||||
await write_audit_log(
|
||||
db,
|
||||
"service.info.check",
|
||||
user.id,
|
||||
{
|
||||
"latest_version": row.latest_version,
|
||||
"latest_ref": row.release_check_url,
|
||||
"update_available": row.update_available,
|
||||
"last_check_error": row.last_check_error,
|
||||
},
|
||||
)
|
||||
return ServiceInfoCheckResult(
|
||||
latest_version=row.latest_version,
|
||||
latest_ref=(row.release_check_url or None),
|
||||
update_available=row.update_available,
|
||||
last_checked_at=row.last_checked_at or check_time,
|
||||
last_check_error=row.last_check_error,
|
||||
)
|
||||
Reference in New Issue
Block a user