Release 1.0.0 with server info console
This commit is contained in:
186
app/core/server_info.py
Normal file
186
app/core/server_info.py
Normal file
@@ -0,0 +1,186 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from datetime import datetime, timezone
|
||||
import os
|
||||
from pathlib import Path
|
||||
import socket
|
||||
|
||||
from app.api.deps import get_master_key
|
||||
|
||||
APP_NAME = "Ignis Core"
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
VERSION_FILE = PROJECT_ROOT / "VERSION"
|
||||
SERVER_STARTED_AT = datetime.now(timezone.utc)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ServerBuildInfo:
|
||||
version: str
|
||||
git_sha: str | None
|
||||
build_date: str | None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ServerUrlInfo:
|
||||
observed_base_url: str | None
|
||||
configured_public_base_url: str | None
|
||||
effective_public_base_url: str | None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ServerConfigurationStatus:
|
||||
configured: bool
|
||||
master_key_configured: bool
|
||||
scan_network_configured: bool
|
||||
public_base_url_configured: bool
|
||||
build_metadata_complete: bool
|
||||
|
||||
|
||||
def _clean_env(name: str) -> str | None:
|
||||
value = os.getenv(name, "").strip()
|
||||
return value or None
|
||||
|
||||
|
||||
def _normalize_url(value: str | None) -> str | None:
|
||||
if not value:
|
||||
return None
|
||||
normalized = value.strip().rstrip("/")
|
||||
return normalized or None
|
||||
|
||||
|
||||
def _read_version_file() -> str | None:
|
||||
try:
|
||||
value = VERSION_FILE.read_text(encoding="utf-8").strip()
|
||||
except OSError:
|
||||
return None
|
||||
return value or None
|
||||
|
||||
|
||||
def get_app_version() -> str:
|
||||
return (
|
||||
_clean_env("IGNIS_BUILD_VERSION")
|
||||
or _read_version_file()
|
||||
or "1.0.0"
|
||||
)
|
||||
|
||||
|
||||
def _resolve_git_ref(git_dir: Path, ref_name: str) -> str | None:
|
||||
ref_path = git_dir / ref_name
|
||||
if ref_path.exists():
|
||||
try:
|
||||
value = ref_path.read_text(encoding="utf-8").strip()
|
||||
except OSError:
|
||||
return None
|
||||
return value or None
|
||||
|
||||
packed_refs = git_dir / "packed-refs"
|
||||
if not packed_refs.exists():
|
||||
return None
|
||||
|
||||
try:
|
||||
lines = packed_refs.read_text(encoding="utf-8").splitlines()
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
for line in lines:
|
||||
if not line or line.startswith("#") or line.startswith("^"):
|
||||
continue
|
||||
sha, _, candidate_ref = line.partition(" ")
|
||||
if candidate_ref.strip() == ref_name:
|
||||
return sha.strip() or None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _read_git_sha() -> str | None:
|
||||
git_dir = PROJECT_ROOT / ".git"
|
||||
if not git_dir.exists():
|
||||
return None
|
||||
|
||||
head_path = git_dir / "HEAD"
|
||||
try:
|
||||
head_value = head_path.read_text(encoding="utf-8").strip()
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
if not head_value:
|
||||
return None
|
||||
|
||||
if head_value.startswith("ref: "):
|
||||
resolved = _resolve_git_ref(git_dir, head_value[5:].strip())
|
||||
if not resolved:
|
||||
return None
|
||||
return resolved[:12]
|
||||
|
||||
return head_value[:12]
|
||||
|
||||
|
||||
def get_build_info() -> ServerBuildInfo:
|
||||
git_sha = _clean_env("IGNIS_GIT_SHA") or _read_git_sha()
|
||||
build_date = _clean_env("IGNIS_BUILD_DATE")
|
||||
return ServerBuildInfo(
|
||||
version=get_app_version(),
|
||||
git_sha=git_sha,
|
||||
build_date=build_date,
|
||||
)
|
||||
|
||||
|
||||
def get_instance_name() -> str:
|
||||
return _clean_env("IGNIS_INSTANCE_NAME") or socket.gethostname()
|
||||
|
||||
|
||||
def get_server_urls(observed_base_url: str | None) -> ServerUrlInfo:
|
||||
configured_public_base_url = _normalize_url(_clean_env("IGNIS_PUBLIC_BASE_URL"))
|
||||
observed = _normalize_url(observed_base_url)
|
||||
return ServerUrlInfo(
|
||||
observed_base_url=observed,
|
||||
configured_public_base_url=configured_public_base_url,
|
||||
effective_public_base_url=configured_public_base_url or observed,
|
||||
)
|
||||
|
||||
|
||||
def get_configuration_status(build_info: ServerBuildInfo) -> ServerConfigurationStatus:
|
||||
master_key_configured = get_master_key() is not None
|
||||
public_base_url_configured = _clean_env("IGNIS_PUBLIC_BASE_URL") is not None
|
||||
scan_network_configured = _clean_env("SCAN_NETWORK") is not None
|
||||
build_metadata_complete = bool(build_info.version and build_info.git_sha and build_info.build_date)
|
||||
return ServerConfigurationStatus(
|
||||
configured=master_key_configured,
|
||||
master_key_configured=master_key_configured,
|
||||
scan_network_configured=scan_network_configured,
|
||||
public_base_url_configured=public_base_url_configured,
|
||||
build_metadata_complete=build_metadata_complete,
|
||||
)
|
||||
|
||||
|
||||
def get_uptime_seconds() -> int:
|
||||
delta = datetime.now(timezone.utc) - SERVER_STARTED_AT
|
||||
return max(int(delta.total_seconds()), 0)
|
||||
|
||||
|
||||
def build_server_info(
|
||||
*,
|
||||
observed_base_url: str | None = None,
|
||||
include_diagnostics: bool,
|
||||
) -> dict:
|
||||
payload = {
|
||||
"app_name": APP_NAME,
|
||||
"uptime_seconds": get_uptime_seconds(),
|
||||
"diagnostics_visible": include_diagnostics,
|
||||
}
|
||||
if not include_diagnostics:
|
||||
return payload
|
||||
|
||||
build_info = get_build_info()
|
||||
payload["instance_name"] = get_instance_name()
|
||||
payload["timezone"] = os.getenv("APP_TIMEZONE", "Asia/Novosibirsk")
|
||||
payload["started_at"] = SERVER_STARTED_AT.isoformat()
|
||||
payload["build"] = {
|
||||
"version": build_info.version,
|
||||
"git_sha": build_info.git_sha,
|
||||
"build_date": build_info.build_date,
|
||||
}
|
||||
payload["urls"] = asdict(get_server_urls(observed_base_url))
|
||||
payload["configuration"] = asdict(get_configuration_status(build_info))
|
||||
return payload
|
||||
Reference in New Issue
Block a user