updated plugin refactor and added FTP and SCP plugins , also hydrusnetwork plugin migration
This commit is contained in:
+147
-1
@@ -1,3 +1,6 @@
|
||||
import datetime
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Any, Optional, Sequence
|
||||
|
||||
from SYS.cmdlet_spec import Cmdlet, CmdletArg
|
||||
@@ -7,17 +10,20 @@ from SYS.config import (
|
||||
save_config_and_verify,
|
||||
set_nested_config_value,
|
||||
)
|
||||
from SYS.database import LOG_DB_PATH, db
|
||||
from SYS.logger import log
|
||||
from SYS import pipeline as ctx
|
||||
from SYS.result_table import Table
|
||||
from cmdnat._parsing import (
|
||||
extract_piped_value as _extract_piped_value,
|
||||
extract_value_arg as _extract_value_arg,
|
||||
has_flag as _has_flag,
|
||||
)
|
||||
|
||||
CMDLET = Cmdlet(
|
||||
name=".config",
|
||||
summary="Manage configuration settings",
|
||||
usage=".config [key] [value]",
|
||||
usage=".config [key] [value] | .config -log [count]",
|
||||
arg=[
|
||||
CmdletArg(
|
||||
name="key",
|
||||
@@ -33,6 +39,140 @@ CMDLET = Cmdlet(
|
||||
)
|
||||
|
||||
|
||||
def _extract_log_limit(args: Sequence[str], default: int = 30) -> int:
|
||||
try:
|
||||
tokens = [str(arg).strip() for arg in (args or []) if str(arg).strip()]
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
for idx, token in enumerate(tokens):
|
||||
lowered = token.lower()
|
||||
if lowered in {"-log", "--log"}:
|
||||
if idx + 1 < len(tokens):
|
||||
candidate = tokens[idx + 1]
|
||||
if candidate and not candidate.startswith("-"):
|
||||
try:
|
||||
return max(1, min(200, int(candidate)))
|
||||
except Exception:
|
||||
return default
|
||||
return default
|
||||
if lowered.startswith("-log=") or lowered.startswith("--log="):
|
||||
_, value = lowered.split("=", 1)
|
||||
try:
|
||||
return max(1, min(200, int(value)))
|
||||
except Exception:
|
||||
return default
|
||||
return default
|
||||
|
||||
|
||||
def _fallback_log_path() -> Path:
|
||||
return Path(db.db_path).with_name("logs") / "log_fallback.txt"
|
||||
|
||||
|
||||
def _load_recent_config_logs(limit: int = 30) -> List[Dict[str, str]]:
|
||||
rows: List[Dict[str, str]] = []
|
||||
sql = """
|
||||
SELECT timestamp, level, module, message
|
||||
FROM logs
|
||||
WHERE lower(module) LIKE ?
|
||||
OR lower(message) LIKE ?
|
||||
OR lower(message) LIKE ?
|
||||
OR lower(message) LIKE ?
|
||||
ORDER BY id DESC
|
||||
LIMIT ?
|
||||
"""
|
||||
params = (
|
||||
"%config%",
|
||||
"%config%",
|
||||
"%save failed%",
|
||||
"%saving configuration failed%",
|
||||
int(limit),
|
||||
)
|
||||
|
||||
try:
|
||||
with sqlite3.connect(str(LOG_DB_PATH), timeout=5.0) as conn:
|
||||
conn.row_factory = sqlite3.Row
|
||||
cur = conn.cursor()
|
||||
cur.execute(sql, params)
|
||||
fetched = cur.fetchall()
|
||||
cur.close()
|
||||
for row in fetched:
|
||||
rows.append(
|
||||
{
|
||||
"timestamp": str(row["timestamp"] or ""),
|
||||
"level": str(row["level"] or ""),
|
||||
"module": str(row["module"] or ""),
|
||||
"message": str(row["message"] or ""),
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
rows = []
|
||||
|
||||
if rows:
|
||||
return rows
|
||||
|
||||
fallback = _fallback_log_path()
|
||||
try:
|
||||
if not fallback.exists():
|
||||
return []
|
||||
lines = fallback.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
matches = [
|
||||
line for line in lines
|
||||
if any(term in line.lower() for term in ("config", "save failed", "saving configuration failed"))
|
||||
]
|
||||
for line in reversed(matches[-limit:]):
|
||||
rows.append(
|
||||
{
|
||||
"timestamp": "",
|
||||
"level": "FALLBACK",
|
||||
"module": "fallback",
|
||||
"message": line,
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
return []
|
||||
return rows
|
||||
|
||||
|
||||
def _format_log_timestamp_local(raw_value: str) -> str:
|
||||
text = str(raw_value or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
for pattern in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M:%S.%f"):
|
||||
try:
|
||||
parsed = datetime.datetime.strptime(text, pattern).replace(tzinfo=datetime.timezone.utc)
|
||||
return parsed.astimezone().strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
continue
|
||||
return text
|
||||
|
||||
|
||||
def _show_config_logs(args: Sequence[str]) -> int:
|
||||
limit = _extract_log_limit(args)
|
||||
rows = _load_recent_config_logs(limit=limit)
|
||||
if not rows:
|
||||
print(
|
||||
f"No recent config/save logs found in {LOG_DB_PATH.name} or {_fallback_log_path().name}."
|
||||
)
|
||||
return 0
|
||||
|
||||
table = Table("Configuration Logs")
|
||||
table.set_table("config.logs")
|
||||
table.set_source_command(".config", ["-log", str(limit)])
|
||||
|
||||
for row_data in rows:
|
||||
row = table.add_row()
|
||||
row.add_column("Time (local)", _format_log_timestamp_local(row_data.get("timestamp", "")))
|
||||
row.add_column("Level", row_data.get("level", ""))
|
||||
row.add_column("Module", row_data.get("module", ""))
|
||||
row.add_column("Message", row_data.get("message", ""))
|
||||
|
||||
ctx.set_last_result_table_overlay(table, rows)
|
||||
ctx.set_current_stage_table(table)
|
||||
print(f"Showing {len(rows)} recent configuration log entries.")
|
||||
return 0
|
||||
|
||||
|
||||
def flatten_config(config: Dict[str, Any], parent_key: str = "", sep: str = ".") -> List[Dict[str, Any]]:
|
||||
items: List[Dict[str, Any]] = []
|
||||
for k, v in config.items():
|
||||
@@ -108,6 +248,9 @@ def _strip_value_quotes(value: str) -> str:
|
||||
def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
||||
import sys
|
||||
|
||||
if _has_flag(args, "-log") or _has_flag(args, "--log"):
|
||||
return _show_config_logs(args)
|
||||
|
||||
# Load configuration from the database
|
||||
current_config = load_config()
|
||||
|
||||
@@ -135,6 +278,7 @@ def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
||||
try:
|
||||
save_config_and_verify(current_config)
|
||||
except Exception as exc:
|
||||
log(f"Configuration save verification failed for '{selection_key}': {exc}")
|
||||
print(f"Error saving configuration (verification failed): {exc}")
|
||||
return 1
|
||||
else:
|
||||
@@ -142,6 +286,7 @@ def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
||||
print(f"Updated '{selection_key}' to '{new_value}'")
|
||||
return 0
|
||||
except Exception as exc:
|
||||
log(f"Error updating config '{selection_key}': {exc}")
|
||||
print(f"Error updating config: {exc}")
|
||||
return 1
|
||||
|
||||
@@ -201,6 +346,7 @@ def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
||||
print(f"Updated '{key}' to '{value}'")
|
||||
return 0
|
||||
except Exception as exc:
|
||||
log(f"Error updating config '{key}': {exc}")
|
||||
print(f"Error updating config: {exc}")
|
||||
return 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user