update check active

This commit is contained in:
2026-07-20 16:08:36 +07:00
parent 4a159cad71
commit db1ce68800
17 changed files with 1230 additions and 73 deletions

View File

@@ -1,6 +1,8 @@
from __future__ import annotations
import sqlite3
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
from app.config import settings
@@ -55,6 +57,8 @@ CREATE TABLE IF NOT EXISTS installed_components (
package_name TEXT,
package_version TEXT,
service_name TEXT,
service_check_status TEXT DEFAULT 'not-checked',
service_checks_json TEXT,
docker_image TEXT,
docker_digest TEXT,
container_name TEXT,
@@ -75,6 +79,8 @@ CREATE TABLE IF NOT EXISTS task_components (
progress INTEGER DEFAULT 0,
current_step TEXT,
error_message TEXT,
service_check_status TEXT DEFAULT 'not-checked',
service_checks_json TEXT,
started_at TEXT,
finished_at TEXT,
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
@@ -96,10 +102,18 @@ def _ensure_column(connection: sqlite3.Connection, table: str, column: str, defi
connection.execute(f"ALTER TABLE {table} ADD COLUMN {column} {definition}")
def get_connection() -> sqlite3.Connection:
@contextmanager
def get_connection() -> Iterator[sqlite3.Connection]:
connection = sqlite3.connect(settings.db_path, timeout=30)
connection.row_factory = sqlite3.Row
return connection
try:
yield connection
connection.commit()
except Exception:
connection.rollback()
raise
finally:
connection.close()
def initialize_database() -> None:
@@ -110,3 +124,17 @@ def initialize_database() -> None:
with get_connection() as connection:
connection.executescript(SCHEMA)
_ensure_column(connection, "installed_apps", "open_url", "TEXT")
_ensure_column(
connection,
"installed_components",
"service_check_status",
"TEXT DEFAULT 'not-checked'",
)
_ensure_column(connection, "installed_components", "service_checks_json", "TEXT")
_ensure_column(
connection,
"task_components",
"service_check_status",
"TEXT DEFAULT 'not-checked'",
)
_ensure_column(connection, "task_components", "service_checks_json", "TEXT")

View File

@@ -17,6 +17,29 @@ def row_to_dict(row: Any) -> dict[str, Any] | None:
return dict(row)
def _encode_service_checks(value: list[dict[str, Any]]) -> str:
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
def _decode_service_checks(value: Any) -> list[dict[str, Any]]:
if not value:
return []
try:
decoded = json.loads(str(value))
except (TypeError, ValueError, json.JSONDecodeError):
return []
if not isinstance(decoded, list):
return []
return [item for item in decoded if isinstance(item, dict)]
def _component_row(row: Any) -> dict[str, Any]:
item = dict(row)
item["service_checks"] = _decode_service_checks(item.pop("service_checks_json", None))
item["service_check_status"] = item.get("service_check_status") or "not-checked"
return item
class Repository:
def create_task(self, task_id: str, task_type: str, app_id: str, app_name: str | None) -> None:
now = utc_now()
@@ -118,6 +141,8 @@ class Repository:
progress: int | None = None,
current_step: str | None = None,
error_message: str | None = None,
service_check_status: str | None = None,
service_checks: list[dict[str, Any]] | None = None,
started_at: str | None = None,
finished_at: str | None = None,
) -> None:
@@ -128,6 +153,12 @@ class Repository:
"progress": progress,
"current_step": current_step,
"error_message": error_message,
"service_check_status": service_check_status,
"service_checks_json": (
_encode_service_checks(service_checks)
if service_checks is not None
else None
),
"started_at": started_at,
"finished_at": finished_at,
}.items():
@@ -147,14 +178,15 @@ class Repository:
with get_connection() as connection:
rows = connection.execute(
"""
SELECT component_id, type, status, progress, current_step, error_message, started_at, finished_at
SELECT component_id, type, status, progress, current_step, error_message,
service_check_status, service_checks_json, started_at, finished_at
FROM task_components
WHERE task_id = ?
ORDER BY install_order ASC, id ASC
""",
(task_id,),
).fetchall()
return [dict(row) for row in rows]
return [_component_row(row) for row in rows]
def list_installed_apps(self) -> list[dict[str, Any]]:
with get_connection() as connection:
@@ -205,10 +237,11 @@ class Repository:
"""
INSERT INTO installed_components (
app_id, component_id, type, install_order, status, package_name, package_version,
service_name, docker_image, docker_digest, container_name, compose_project_name,
service_name, service_check_status, service_checks_json,
docker_image, docker_digest, container_name, compose_project_name,
installed_at, updated_at
)
VALUES (?, ?, ?, ?, 'installed', ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, 'installed', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(app_id, component_id) DO UPDATE SET
type = excluded.type,
install_order = excluded.install_order,
@@ -216,6 +249,8 @@ class Repository:
package_name = excluded.package_name,
package_version = excluded.package_version,
service_name = excluded.service_name,
service_check_status = excluded.service_check_status,
service_checks_json = excluded.service_checks_json,
docker_image = excluded.docker_image,
docker_digest = excluded.docker_digest,
container_name = excluded.container_name,
@@ -230,6 +265,8 @@ class Repository:
component.get("packageName"),
component.get("version"),
component.get("serviceName"),
component.get("serviceCheckStatus", "not-checked"),
_encode_service_checks(component.get("serviceChecks", [])),
component.get("image"),
component.get("digest"),
component.get("containerName"),
@@ -250,7 +287,7 @@ class Repository:
""",
(app_id,),
).fetchall()
return [dict(row) for row in rows]
return [_component_row(row) for row in rows]
def export_manifest_hash(self, manifest: dict[str, Any]) -> str:
return json.dumps(manifest, sort_keys=True, separators=(",", ":"))