minor fix in backup pane token validation
This commit is contained in:
parent
cf969120bf
commit
c4a6e18e2b
4 changed files with 108 additions and 63 deletions
|
|
@ -1166,25 +1166,43 @@ class BackupReader:
|
||||||
"server": False,
|
"server": False,
|
||||||
"bot_name": None,
|
"bot_name": None,
|
||||||
"server_name": None,
|
"server_name": None,
|
||||||
"intents": {"message_content": True},
|
"intents": {
|
||||||
"permissions": {"view_channel": True, "read_message_history": True},
|
"message_content": True,
|
||||||
|
"members": True
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"view_channel": True,
|
||||||
|
"read_messages": True,
|
||||||
|
"read_message_history": True
|
||||||
|
},
|
||||||
|
"error": None
|
||||||
}
|
}
|
||||||
|
|
||||||
bp = self.backup_path
|
bp = self.backup_path
|
||||||
db_path = bp / "backup.db"
|
db_path = bp / "backup.db"
|
||||||
|
|
||||||
if db_path.exists():
|
if not bp.exists():
|
||||||
try:
|
results["error"] = f"Backup folder not found: {bp}"
|
||||||
# Use sub-connection to validate
|
return results
|
||||||
db = BackupDatabase(db_path)
|
|
||||||
profile = db.get_guild_profile()
|
if not db_path.exists():
|
||||||
if profile:
|
results["error"] = f"backup.db not found in {bp}"
|
||||||
results["token"] = True
|
return results
|
||||||
results["server"] = True
|
|
||||||
results["bot_name"] = "LOCAL BACKUP"
|
try:
|
||||||
results["server_name"] = profile.get("name", "Unknown")
|
# Use sub-connection to validate
|
||||||
except Exception:
|
from src.core.backup_database import BackupDatabase
|
||||||
pass
|
db = BackupDatabase(db_path)
|
||||||
|
profile = db.get_guild_profile()
|
||||||
|
if profile:
|
||||||
|
results["token"] = True
|
||||||
|
results["server"] = True
|
||||||
|
results["bot_name"] = "LOCAL BACKUP"
|
||||||
|
results["server_name"] = profile.get("name", "Unknown")
|
||||||
|
else:
|
||||||
|
results["error"] = "Backup profile (guild_profile table) is empty."
|
||||||
|
except Exception as e:
|
||||||
|
results["error"] = f"Database error: {str(e)}"
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,25 +50,36 @@ class MigrationContext:
|
||||||
|
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
|
||||||
def _find_backup_path(self, server_id: str, base_dir_str: str) -> Path:
|
def _find_backup_path(self, server_id: str | int | None, base_dir_str: str) -> Path:
|
||||||
"""Searches workspace for a DISCORD_BACKUP-{server_id} directory. Returns the path (does not create)."""
|
"""Searches workspace for a DISCORD_BACKUP-{server_id} directory. Returns the path (does not create)."""
|
||||||
|
if not server_id:
|
||||||
|
return Path(base_dir_str or ".") / "DISCORD_BACKUP-UNKNOWN"
|
||||||
|
|
||||||
|
sid_str = str(server_id).strip()
|
||||||
base_dir = Path(base_dir_str) if base_dir_str else Path(".")
|
base_dir = Path(base_dir_str) if base_dir_str else Path(".")
|
||||||
|
|
||||||
# 1. Search inside the specific workspace directory first
|
# 1. Search inside the specific workspace directory first
|
||||||
if base_dir.exists() and base_dir.is_dir():
|
if base_dir.exists() and base_dir.is_dir():
|
||||||
for d in base_dir.iterdir():
|
for d in base_dir.iterdir():
|
||||||
if d.is_dir() and d.name.endswith(f"-{server_id}") and "DISCORD_BACKUP" in d.name:
|
if d.is_dir():
|
||||||
logger.info(f"Found backup directory: {d}")
|
dname = d.name.upper()
|
||||||
return d
|
if "DISCORD_BACKUP" in dname and dname.endswith(f"-{sid_str}"):
|
||||||
|
logger.info(f"Found backup directory: {d}")
|
||||||
|
return d
|
||||||
|
|
||||||
# 2. Fallback to global search if it wasn't found in the workspace
|
# 2. Fallback to global search if it wasn't found in the workspace
|
||||||
for d in Path(".").rglob(f"DISCORD_BACKUP-{server_id}"):
|
# We use a pattern that matches the name directly to be faster than rglob("*")
|
||||||
|
pattern = f"DISCORD_BACKUP-{sid_str}"
|
||||||
|
for d in Path(".").rglob(pattern):
|
||||||
if d.is_dir():
|
if d.is_dir():
|
||||||
logger.info(f"Found backup directory globally: {d}")
|
logger.info(f"Found backup directory globally: {d}")
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
# 3. Last ditch: some backups might not have the DISCORD_BACKUP prefix in some forks/versions?
|
||||||
|
# Unlikely here, so we stick to the return
|
||||||
|
|
||||||
# If not found anywhere, return the expected location inside the workspace
|
# If not found anywhere, return the expected location inside the workspace
|
||||||
new_path = base_dir / f"DISCORD_BACKUP-{server_id}"
|
new_path = base_dir / f"DISCORD_BACKUP-{sid_str}"
|
||||||
logger.info(f"Using lazy backup path: {new_path}")
|
logger.info(f"Using lazy backup path: {new_path}")
|
||||||
return new_path
|
return new_path
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class AppConfig(BaseModel):
|
||||||
stoat_server_id: Optional[str] = Field(default=None)
|
stoat_server_id: Optional[str] = Field(default=None)
|
||||||
stoat_api_url: Optional[str] = Field(default=None)
|
stoat_api_url: Optional[str] = Field(default=None)
|
||||||
anonymize_users: bool = Field(default=False)
|
anonymize_users: bool = Field(default=False)
|
||||||
log_level: str = Field(default="DEBUG")
|
log_level: str = Field(default="INFO")
|
||||||
|
|
||||||
# ── backward‑compat shims (read‑only) ────────────────────────────────
|
# ── backward‑compat shims (read‑only) ────────────────────────────────
|
||||||
# The rest of the codebase (fluxer/stoat modules) still reads these.
|
# The rest of the codebase (fluxer/stoat modules) still reads these.
|
||||||
|
|
@ -52,8 +52,8 @@ def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing
|
||||||
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
||||||
|
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
save_config(config, path)
|
# DO NOT auto-save here, it might overwrite valid data if path is transiently wrong
|
||||||
print(f"Created default configuration: {config_path}")
|
# print(f"Created default configuration: {config_path}")
|
||||||
return config
|
return config
|
||||||
|
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,10 @@ class OperationPane(Container):
|
||||||
self.target_platform = self.config.target_platform or "fluxer"
|
self.target_platform = self.config.target_platform or "fluxer"
|
||||||
self.engine: MigrationContext | None = None
|
self.engine: MigrationContext | None = None
|
||||||
self.exporter: DiscordExporter | None = None
|
self.exporter: DiscordExporter | None = None
|
||||||
self.validation_results: dict = {}
|
self.validation_results: dict = {
|
||||||
|
"discord_validating": True,
|
||||||
|
"target_validating": True,
|
||||||
|
}
|
||||||
self.tokens_valid = False
|
self.tokens_valid = False
|
||||||
self.permissions_complete = False
|
self.permissions_complete = False
|
||||||
self.has_backup = False
|
self.has_backup = False
|
||||||
|
|
@ -162,6 +165,7 @@ class OperationPane(Container):
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
self._rebuild_engine()
|
self._rebuild_engine()
|
||||||
|
self._update_info_labels()
|
||||||
# Wait for DOM to be stable before first validation
|
# Wait for DOM to be stable before first validation
|
||||||
self.call_after_refresh(self.run_validate)
|
self.call_after_refresh(self.run_validate)
|
||||||
|
|
||||||
|
|
@ -221,6 +225,7 @@ class OperationPane(Container):
|
||||||
|
|
||||||
# ── labels ────────────────────────────────────────────────────────────
|
# ── labels ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# ── labels ────────────────────────────────────────────────────────────
|
||||||
def _update_info_labels(self):
|
def _update_info_labels(self):
|
||||||
if not self.is_mounted:
|
if not self.is_mounted:
|
||||||
return
|
return
|
||||||
|
|
@ -230,7 +235,8 @@ class OperationPane(Container):
|
||||||
d_name = v.get("discord_server_name")
|
d_name = v.get("discord_server_name")
|
||||||
d_bot = v.get("discord_bot_name")
|
d_bot = v.get("discord_bot_name")
|
||||||
|
|
||||||
if v.get("discord_validating"):
|
is_val_d = v.get("discord_validating") or v.get("discord_token") is None
|
||||||
|
if is_val_d:
|
||||||
s_disp, b_disp = "[yellow]Validating...[/yellow]", "[yellow]Validating...[/yellow]"
|
s_disp, b_disp = "[yellow]Validating...[/yellow]", "[yellow]Validating...[/yellow]"
|
||||||
elif v.get("discord_timeout"):
|
elif v.get("discord_timeout"):
|
||||||
s_disp, b_disp = "[red]TIMEOUT[/red]", "[red]TIMEOUT[/red]"
|
s_disp, b_disp = "[red]TIMEOUT[/red]", "[red]TIMEOUT[/red]"
|
||||||
|
|
@ -241,14 +247,15 @@ class OperationPane(Container):
|
||||||
s_disp, b_disp = "[red]SERVER NOT SELECTED[/red]", f"[green]{d_bot}[/green]"
|
s_disp, b_disp = "[red]SERVER NOT SELECTED[/red]", f"[green]{d_bot}[/green]"
|
||||||
elif v.get("discord_token") is False:
|
elif v.get("discord_token") is False:
|
||||||
if self.config.tool_mode == "backup_transfer" and self.view_mode == "shuttle":
|
if self.config.tool_mode == "backup_transfer" and self.view_mode == "shuttle":
|
||||||
# Check if it's missing because no server was selected
|
if not self.config.discord_server_id:
|
||||||
fillers = ["DISCORD_SERVER_ID", "000000000000000000", "", None]
|
|
||||||
if self.config.discord_server_id in fillers:
|
|
||||||
s_disp, b_disp = "[red]SERVER NOT SELECTED[/red]", "[red]SERVER NOT SELECTED[/red]"
|
s_disp, b_disp = "[red]SERVER NOT SELECTED[/red]", "[red]SERVER NOT SELECTED[/red]"
|
||||||
else:
|
else:
|
||||||
s_disp, b_disp = "[red]NOT FOUND[/red]", "[red]NOT FOUND[/red]"
|
s_disp, b_disp = "[red]NOT FOUND[/red]", "[red]NOT FOUND[/red]"
|
||||||
else:
|
else:
|
||||||
s_disp, b_disp = "[red]INVALID TOKEN[/red]", "[red]INVALID TOKEN[/red]"
|
if not self.config.discord_bot_token:
|
||||||
|
s_disp, b_disp = "[red]NOT SET UP[/red]", "[red]NOT SET UP[/red]"
|
||||||
|
else:
|
||||||
|
s_disp, b_disp = "[red]INVALID TOKEN[/red]", "[red]INVALID TOKEN[/red]"
|
||||||
else:
|
else:
|
||||||
s_disp, b_disp = "[red]NOT SET UP[/red]", "[red]NOT SET UP[/red]"
|
s_disp, b_disp = "[red]NOT SET UP[/red]", "[red]NOT SET UP[/red]"
|
||||||
|
|
||||||
|
|
@ -274,7 +281,7 @@ class OperationPane(Container):
|
||||||
if not dp.get("read_messages"): d_missing.append("Read Messages")
|
if not dp.get("read_messages"): d_missing.append("Read Messages")
|
||||||
if not dp.get("read_message_history"): d_missing.append("Read Message History")
|
if not dp.get("read_message_history"): d_missing.append("Read Message History")
|
||||||
|
|
||||||
if v.get("discord_validating"):
|
if is_val_d:
|
||||||
d_status = ""
|
d_status = ""
|
||||||
for ldr in self.query("#op_d_loader"): ldr.display = True
|
for ldr in self.query("#op_d_loader"): ldr.display = True
|
||||||
for lbl in self.query("#op_lbl_d_status"): lbl.display = False
|
for lbl in self.query("#op_lbl_d_status"): lbl.display = False
|
||||||
|
|
@ -288,15 +295,17 @@ class OperationPane(Container):
|
||||||
d_status = "[red]SERVER NOT SET[/red]"
|
d_status = "[red]SERVER NOT SET[/red]"
|
||||||
elif v.get("discord_timeout"):
|
elif v.get("discord_timeout"):
|
||||||
d_status = "[red]TIMEOUT[/red]"
|
d_status = "[red]TIMEOUT[/red]"
|
||||||
elif d_err:
|
if d_err:
|
||||||
d_status = f"[red]{d_err}[/red]"
|
d_status = f"[red]{d_err}[/red]"
|
||||||
elif d_missing:
|
elif d_missing:
|
||||||
d_status = f"[yellow]MISSING: {', '.join(d_missing)}[/yellow]"
|
d_status = f"[yellow]MISSING: {', '.join(d_missing)}[/yellow]"
|
||||||
else:
|
elif v.get("discord_token") is False:
|
||||||
d_status = "[red]INVALID[/red]"
|
d_status = "[red]INVALID[/red]"
|
||||||
|
else:
|
||||||
|
d_status = ""
|
||||||
|
|
||||||
if d_status:
|
for lbl in self.query("#op_lbl_d_status"):
|
||||||
for lbl in self.query("#op_lbl_d_status"): lbl.update(f"{d_status}")
|
lbl.update(f"{d_status}")
|
||||||
|
|
||||||
# Target / Backup Info
|
# Target / Backup Info
|
||||||
if self.view_mode == "backup":
|
if self.view_mode == "backup":
|
||||||
|
|
@ -329,7 +338,8 @@ class OperationPane(Container):
|
||||||
|
|
||||||
for lbl in self.query("#op_lbl_t_header"): lbl.update(plat)
|
for lbl in self.query("#op_lbl_t_header"): lbl.update(plat)
|
||||||
|
|
||||||
if v.get("target_validating"):
|
is_val_t = v.get("target_validating") or v.get("target_token") is None
|
||||||
|
if is_val_t:
|
||||||
c_disp, tb_disp = "[yellow]Validating...[/yellow]", "[yellow]Validating...[/yellow]"
|
c_disp, tb_disp = "[yellow]Validating...[/yellow]", "[yellow]Validating...[/yellow]"
|
||||||
elif v.get("target_timeout"):
|
elif v.get("target_timeout"):
|
||||||
c_disp, tb_disp = "[red]TIMEOUT[/red]", "[red]TIMEOUT[/red]"
|
c_disp, tb_disp = "[red]TIMEOUT[/red]", "[red]TIMEOUT[/red]"
|
||||||
|
|
@ -353,7 +363,7 @@ class OperationPane(Container):
|
||||||
if tp:
|
if tp:
|
||||||
t_missing = [k.replace('_', ' ').title() for k, val_p in tp.items() if not val_p]
|
t_missing = [k.replace('_', ' ').title() for k, val_p in tp.items() if not val_p]
|
||||||
|
|
||||||
if v.get("target_validating"):
|
if is_val_t:
|
||||||
t_status = ""
|
t_status = ""
|
||||||
for ldr in self.query("#op_t_loader"): ldr.display = True
|
for ldr in self.query("#op_t_loader"): ldr.display = True
|
||||||
for lbl in self.query("#op_lbl_t_status"): lbl.display = False
|
for lbl in self.query("#op_lbl_t_status"): lbl.display = False
|
||||||
|
|
@ -369,11 +379,13 @@ class OperationPane(Container):
|
||||||
t_status = f"ERROR: [red]{t_err}[/red]"
|
t_status = f"ERROR: [red]{t_err}[/red]"
|
||||||
elif t_missing:
|
elif t_missing:
|
||||||
t_status = f"[yellow]MISSING: {', '.join(t_missing)} Permission[/yellow]"
|
t_status = f"[yellow]MISSING: {', '.join(t_missing)} Permission[/yellow]"
|
||||||
else:
|
elif v.get("target_token") is False:
|
||||||
t_status = "ERROR: [red]INVALID[/red]"
|
t_status = "ERROR: [red]INVALID[/red]"
|
||||||
|
else:
|
||||||
if t_status:
|
t_status = ""
|
||||||
for lbl in self.query("#op_lbl_t_status"): lbl.update(f"{t_status}")
|
|
||||||
|
for lbl in self.query("#op_lbl_t_status"):
|
||||||
|
lbl.update(f"{t_status}")
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
for bid in ("#op_clone", "#op_sync", "#op_messages", "#op_danger"):
|
for bid in ("#op_clone", "#op_sync", "#op_messages", "#op_danger"):
|
||||||
|
|
@ -385,6 +397,7 @@ class OperationPane(Container):
|
||||||
async def run_validate(self) -> None:
|
async def run_validate(self) -> None:
|
||||||
if not self.is_mounted:
|
if not self.is_mounted:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
plat = "Fluxer" if self.target_platform == "fluxer" else "Stoat"
|
plat = "Fluxer" if self.target_platform == "fluxer" else "Stoat"
|
||||||
# Use query().first() or check presence to avoid NoMatches crashes
|
# Use query().first() or check presence to avoid NoMatches crashes
|
||||||
|
|
@ -405,14 +418,14 @@ class OperationPane(Container):
|
||||||
logger.error(f"Error in run_validate setup: {e}")
|
logger.error(f"Error in run_validate setup: {e}")
|
||||||
|
|
||||||
self.validation_results = {
|
self.validation_results = {
|
||||||
"discord_validating": False,
|
"discord_validating": True,
|
||||||
"target_validating": False,
|
"target_validating": True,
|
||||||
"discord_token": False, "discord_bot_name": None,
|
"discord_token": None, "discord_bot_name": None,
|
||||||
"discord_server": False, "discord_server_name": None,
|
"discord_server": None, "discord_server_name": None,
|
||||||
"discord_intents": {}, "discord_permissions": {},
|
"discord_intents": {}, "discord_permissions": {},
|
||||||
"discord_error": None,
|
"discord_error": None,
|
||||||
"target_token": False, "target_bot_name": None,
|
"target_token": None, "target_bot_name": None,
|
||||||
"target_community": False, "target_community_name": None,
|
"target_community": None, "target_community_name": None,
|
||||||
"target_permissions": {},
|
"target_permissions": {},
|
||||||
"target_error": None,
|
"target_error": None,
|
||||||
"discord_timeout": False, "target_timeout": False,
|
"discord_timeout": False, "target_timeout": False,
|
||||||
|
|
@ -422,31 +435,34 @@ class OperationPane(Container):
|
||||||
self.permissions_complete = False
|
self.permissions_complete = False
|
||||||
self.has_backup = False
|
self.has_backup = False
|
||||||
|
|
||||||
fillers = [
|
# Check what we have
|
||||||
"DISCORD_BOT_TOKEN", "FLUXER_BOT_TOKEN", "STOAT_BOT_TOKEN",
|
has_d_token = bool(self.config.discord_bot_token)
|
||||||
"TARGET_BOT_TOKEN",
|
has_d_server = bool(self.config.discord_server_id)
|
||||||
"000000000000000000", "DISCORD_SERVER_ID", "FLUXER_COMMUNITY_ID",
|
has_t_token = bool(self.config.target_bot_token)
|
||||||
"STOAT_SERVER_ID", "TARGET_SERVER_ID", "", None,
|
has_t_server = bool(self.config.target_server_id)
|
||||||
]
|
|
||||||
|
|
||||||
# Check dummies
|
|
||||||
d_token_dummy = self.config.discord_bot_token in fillers
|
|
||||||
d_server_dummy = self.config.discord_server_id in fillers
|
|
||||||
|
|
||||||
t_token_dummy = (self.config.target_bot_token or "") in fillers
|
|
||||||
t_server_dummy = (self.config.target_server_id or "") in fillers
|
|
||||||
|
|
||||||
# Flag which operations are being validated
|
# Flag which operations are being validated
|
||||||
validating_discord = False
|
validating_discord = False
|
||||||
validating_target = False
|
validating_target = False
|
||||||
|
|
||||||
|
# 1. Determine Discord validating status
|
||||||
if self.config.tool_mode == "backup_transfer" and self.view_mode == "shuttle":
|
if self.config.tool_mode == "backup_transfer" and self.view_mode == "shuttle":
|
||||||
if not d_server_dummy: validating_discord = True
|
if has_d_server:
|
||||||
|
validating_discord = True
|
||||||
|
else:
|
||||||
|
self.validation_results["discord_token"] = False
|
||||||
|
self.validation_results["discord_server"] = False
|
||||||
else:
|
else:
|
||||||
if not d_token_dummy: validating_discord = True
|
if has_d_token:
|
||||||
|
validating_discord = True
|
||||||
if self.view_mode == "shuttle" and not t_token_dummy:
|
else:
|
||||||
|
self.validation_results["discord_token"] = False
|
||||||
|
|
||||||
|
# 2. Determine Target validating status
|
||||||
|
if self.view_mode == "shuttle" and has_t_token:
|
||||||
validating_target = True
|
validating_target = True
|
||||||
|
elif self.view_mode == "shuttle":
|
||||||
|
self.validation_results["target_token"] = False
|
||||||
|
|
||||||
self.validation_results["discord_validating"] = validating_discord
|
self.validation_results["discord_validating"] = validating_discord
|
||||||
self.validation_results["target_validating"] = validating_target
|
self.validation_results["target_validating"] = validating_target
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue