fix(modpack-checker): Code review fixes — license, safety, and polish

Fixes 10 issues from full code review:
- License corrected from MIT to Commercial
- Deprecated datetime.utcnow() replaced with timezone-aware alternative
- PHP array bounds checks added for all platform API responses
- Modrinth file detection now derives project slug instead of using MC version
- validate_api_key() no longer swallows network errors
- HTTP timeouts added to all external API calls in PHP
- Empty API key rejection added to CLI
- Corrupted config now warns on stderr instead of failing silently
- Error response format made consistent across controller
- Docs updated with correct repo URL and clearer CurseForge ID instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude (Chronicler #83 - The Compiler)
2026-04-12 13:37:26 -05:00
parent c6d40dcf39
commit 3457b87aef
8 changed files with 40 additions and 27 deletions

View File

@@ -80,8 +80,12 @@ def config_group() -> None:
@click.argument("api_key")
def config_set_key(api_key: str) -> None:
"""Save your CurseForge API key and validate it."""
api_key = api_key.strip()
if not api_key:
console.print("[red]Error:[/red] API key cannot be empty.")
sys.exit(1)
cfg = Config.load()
cfg.curseforge_api_key = api_key.strip()
cfg.curseforge_api_key = api_key
cfg.save()
client = CurseForgeClient(api_key)

View File

@@ -31,7 +31,11 @@ class Config(BaseModel):
data = json.load(f)
return cls(**data)
except (json.JSONDecodeError, ValueError):
# Corrupted config — fall back to defaults silently
import sys
print(
f"Warning: config file corrupted ({CONFIG_FILE}), using defaults.",
file=sys.stderr,
)
return cls()
return cls()

View File

@@ -130,7 +130,7 @@ class CurseForgeClient:
try:
self._get(f"/v1/games/{self._MINECRAFT_GAME_ID}")
return True
except (CurseForgeAuthError, CurseForgeError):
except CurseForgeAuthError:
return False
def get_mod(self, mod_id: int) -> Dict[str, Any]:

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import List, Optional
@@ -164,11 +164,11 @@ class Database:
if row is None:
raise ValueError(f"Modpack {curseforge_id} not found in database.")
row.current_version = version
row.last_checked = datetime.utcnow()
row.last_checked = datetime.now(timezone.utc)
session.add(
_CheckHistoryRow(
modpack_id=row.id,
checked_at=datetime.utcnow(),
checked_at=datetime.now(timezone.utc),
version_found=version,
notification_sent=notification_sent,
)