Fixed Launch Script / Fixed Site List / Fixed Database Behavior

This commit is contained in:
2025-11-13 21:15:31 -07:00
parent b3f418323c
commit 0bf2e2f099
9 changed files with 153 additions and 62 deletions

View File

@@ -95,13 +95,13 @@ class AssemblyDatabaseManager:
"""Ensure all databases exist, apply schema, and mirror to the runtime directory."""
for domain in AssemblyDomain:
self._ensure_runtime_database(domain)
conn = self._open_connection(domain)
try:
self._apply_schema(conn)
conn.commit()
finally:
conn.close()
self._mirror_database(domain)
def reset_domain(self, domain: AssemblyDomain) -> None:
"""Remove all assemblies and payload metadata for the specified domain."""
@@ -284,15 +284,43 @@ class AssemblyDatabaseManager:
conn.commit()
finally:
conn.close()
self._mirror_database(domain)
# ------------------------------------------------------------------
# Internal helpers
# ------------------------------------------------------------------
def _ensure_runtime_database(self, domain: AssemblyDomain) -> None:
"""Ensure the runtime database exists by copying the staging asset when needed."""
paths = self._paths[domain]
runtime_db = paths.runtime
staging_db = paths.staging
runtime_db.parent.mkdir(parents=True, exist_ok=True)
if runtime_db.exists():
return
if staging_db.exists():
for suffix in ("", "-wal", "-shm"):
staging_candidate = staging_db.parent / f"{staging_db.name}{suffix}"
runtime_candidate = runtime_db.parent / f"{runtime_db.name}{suffix}"
if staging_candidate.exists():
try:
shutil.copy2(staging_candidate, runtime_candidate)
except Exception as exc: # pragma: no cover - best effort seed
self._logger.debug(
"Failed to seed runtime assembly database file %s -> %s: %s",
staging_candidate,
runtime_candidate,
exc,
)
return
runtime_db.touch()
def _open_connection(self, domain: AssemblyDomain, *, readonly: bool = False) -> sqlite3.Connection:
self._ensure_runtime_database(domain)
paths = self._paths[domain]
flags = "ro" if readonly else "rwc"
uri = f"file:{paths.staging.as_posix()}?mode={flags}&cache=shared"
uri = f"file:{paths.runtime.as_posix()}?mode={flags}&cache=shared"
conn = sqlite3.connect(uri, uri=True, timeout=30)
if readonly:
conn.isolation_level = None
@@ -313,26 +341,6 @@ class AssemblyDatabaseManager:
cur.execute(statement)
conn.commit()
def _mirror_database(self, domain: AssemblyDomain) -> None:
paths = self._paths[domain]
staging_db = paths.staging
runtime_db = paths.runtime
runtime_db.parent.mkdir(parents=True, exist_ok=True)
for suffix in ("", "-wal", "-shm"):
staging_candidate = staging_db.parent / f"{staging_db.name}{suffix}"
runtime_candidate = runtime_db.parent / f"{runtime_db.name}{suffix}"
if staging_candidate.exists():
try:
shutil.copy2(staging_candidate, runtime_candidate)
except Exception as exc: # pragma: no cover - best effort mirror
self._logger.debug(
"Failed to mirror assembly database file %s -> %s: %s",
staging_candidate,
runtime_candidate,
exc,
)
def _migrate_legacy_schema(self, cur: sqlite3.Cursor) -> None:
"""Upgrade legacy assembly/payload tables to the consolidated schema."""

View File

@@ -41,7 +41,7 @@ class PayloadManager:
assembly_guid: Optional[str] = None,
extension: Optional[str] = None,
) -> PayloadDescriptor:
"""Persist payload content and mirror it to the runtime directory."""
"""Persist payload content inside the runtime directory only."""
resolved_guid = self._normalise_guid(assembly_guid or uuid.uuid4().hex)
resolved_extension = self._normalise_extension(extension or self._default_extension(payload_type))
@@ -49,23 +49,15 @@ class PayloadManager:
data = content.encode("utf-8") if isinstance(content, str) else bytes(content)
checksum = hashlib.sha256(data).hexdigest()
staging_dir = self._payload_dir(self._staging_root, resolved_guid)
runtime_dir = self._payload_dir(self._runtime_root, resolved_guid)
staging_dir.mkdir(parents=True, exist_ok=True)
runtime_dir.mkdir(parents=True, exist_ok=True)
file_name = f"payload{resolved_extension}"
staging_path = staging_dir / file_name
runtime_path = runtime_dir / file_name
with staging_path.open("wb") as handle:
with runtime_path.open("wb") as handle:
handle.write(data)
try:
shutil.copy2(staging_path, runtime_path)
except Exception as exc: # pragma: no cover - best effort mirror
self._logger.debug("Failed to mirror payload %s to runtime copy: %s", resolved_guid, exc)
descriptor = PayloadDescriptor(
assembly_guid=resolved_guid,
payload_type=payload_type,
@@ -85,35 +77,40 @@ class PayloadManager:
checksum = hashlib.sha256(data).hexdigest()
now = _dt.datetime.utcnow()
staging_path = self._payload_dir(self._staging_root, descriptor.assembly_guid) / descriptor.file_name
runtime_path = self._payload_dir(self._runtime_root, descriptor.assembly_guid) / descriptor.file_name
staging_path.parent.mkdir(parents=True, exist_ok=True)
runtime_path.parent.mkdir(parents=True, exist_ok=True)
with staging_path.open("wb") as handle:
with runtime_path.open("wb") as handle:
handle.write(data)
try:
shutil.copy2(staging_path, runtime_path)
except Exception as exc: # pragma: no cover - best effort mirror
self._logger.debug("Failed to mirror payload %s during update: %s", descriptor.assembly_guid, exc)
descriptor.size_bytes = len(data)
descriptor.checksum = checksum
descriptor.updated_at = now
return descriptor
def read_payload_bytes(self, descriptor: PayloadDescriptor) -> bytes:
"""Retrieve payload content from the staging copy."""
"""Retrieve payload content from the runtime copy, falling back to staging if required."""
runtime_path = self._payload_dir(self._runtime_root, descriptor.assembly_guid) / descriptor.file_name
if runtime_path.exists():
return runtime_path.read_bytes()
staging_path = self._payload_dir(self._staging_root, descriptor.assembly_guid) / descriptor.file_name
return staging_path.read_bytes()
if staging_path.exists():
data = staging_path.read_bytes()
try:
shutil.copy2(staging_path, runtime_path)
except Exception as exc: # pragma: no cover - best effort seed
self._logger.debug("Failed to seed runtime payload %s from staging: %s", descriptor.assembly_guid, exc)
return data
raise FileNotFoundError(f"Payload content missing for {descriptor.assembly_guid}")
def ensure_runtime_copy(self, descriptor: PayloadDescriptor) -> None:
"""Ensure the runtime payload copy matches the staging content."""
"""Ensure the runtime payload copy exists."""
staging_path = self._payload_dir(self._staging_root, descriptor.assembly_guid) / descriptor.file_name
runtime_path = self._payload_dir(self._runtime_root, descriptor.assembly_guid) / descriptor.file_name
if runtime_path.exists():
return
staging_path = self._payload_dir(self._staging_root, descriptor.assembly_guid) / descriptor.file_name
if not staging_path.exists():
self._logger.warning("Payload missing on disk; guid=%s path=%s", descriptor.assembly_guid, staging_path)
return
@@ -124,20 +121,19 @@ class PayloadManager:
self._logger.debug("Failed to mirror payload %s via ensure_runtime_copy: %s", descriptor.assembly_guid, exc)
def delete_payload(self, descriptor: PayloadDescriptor) -> None:
"""Remove staging and runtime payload files."""
"""Remove runtime payload files without mutating staging copies."""
for root in (self._staging_root, self._runtime_root):
dir_path = self._payload_dir(root, descriptor.assembly_guid)
file_path = dir_path / descriptor.file_name
try:
if file_path.exists():
file_path.unlink()
if dir_path.exists() and not any(dir_path.iterdir()):
dir_path.rmdir()
except Exception as exc: # pragma: no cover - best effort cleanup
self._logger.debug(
"Failed to remove payload directory %s (%s): %s", descriptor.assembly_guid, root, exc
)
dir_path = self._payload_dir(self._runtime_root, descriptor.assembly_guid)
file_path = dir_path / descriptor.file_name
try:
if file_path.exists():
file_path.unlink()
if dir_path.exists() and not any(dir_path.iterdir()):
dir_path.rmdir()
except Exception as exc: # pragma: no cover - best effort cleanup
self._logger.debug(
"Failed to remove runtime payload directory %s: %s", descriptor.assembly_guid, exc
)
# ------------------------------------------------------------------
# Helper methods