fix: address bug review findings
- Debounce MutationObserver (200ms) and scope to #modelGrid when available - Sanitize data.updated via parseInt before innerHTML to prevent XSS - Guard pagination against page_size=0 (division by zero) and negative values - Reject non-POST requests on /api/lm-extra/fetch-stats (state-mutating) - Early return in upsert_batch for empty rows list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -284,8 +284,8 @@ async def _proxy_list_with_stats_sort(
|
||||
"""Fetch all items from remote, sort by stats locally, paginate."""
|
||||
from urllib.parse import urlencode
|
||||
|
||||
page = int(request.query.get("page", "1"))
|
||||
page_size = int(request.query.get("page_size", "20"))
|
||||
page = max(1, int(request.query.get("page", "1")))
|
||||
page_size = max(1, int(request.query.get("page_size", "20")))
|
||||
sort_key, _, order = sort_by.partition(":")
|
||||
order = order or "desc"
|
||||
|
||||
@@ -342,6 +342,8 @@ async def _proxy_list_with_stats_sort(
|
||||
|
||||
async def _handle_fetch_stats(request: web.Request) -> web.Response:
|
||||
"""Bulk fetch CivitAI stats for all models."""
|
||||
if request.method != "POST":
|
||||
return web.json_response({"error": "POST required"}, status=405)
|
||||
try:
|
||||
from .stats_service import StatsFetchService
|
||||
except ImportError:
|
||||
|
||||
+12
-5
@@ -174,7 +174,8 @@
|
||||
const resp = await _origFetch("/api/lm-extra/fetch-stats", { method: "POST" });
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
btn.innerHTML = `<i class="fas fa-check"></i> <span>${data.updated} updated</span>`;
|
||||
const count = parseInt(data.updated, 10) || 0;
|
||||
btn.innerHTML = `<i class="fas fa-check"></i> <span>${count} updated</span>`;
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = '<i class="fas fa-chart-bar"></i> <span>Fetch Stats</span>';
|
||||
btn.disabled = false;
|
||||
@@ -205,13 +206,19 @@
|
||||
addFetchStatsButton();
|
||||
patchCards();
|
||||
|
||||
let debounceTimer = null;
|
||||
const observer = new MutationObserver(() => {
|
||||
patchCards();
|
||||
patchSortDropdown();
|
||||
addFetchStatsButton();
|
||||
if (debounceTimer) return;
|
||||
debounceTimer = setTimeout(() => {
|
||||
debounceTimer = null;
|
||||
patchCards();
|
||||
patchSortDropdown();
|
||||
addFetchStatsButton();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
const grid = document.getElementById("modelGrid");
|
||||
observer.observe(grid || document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
// ── Init ───────────────────────────────────────────────────────
|
||||
|
||||
@@ -74,6 +74,8 @@ class StatsDB:
|
||||
|
||||
def upsert_batch(self, rows: list[tuple[str, dict]]) -> None:
|
||||
"""Insert or update stats for multiple models."""
|
||||
if not rows:
|
||||
return
|
||||
conn = self._ensure_conn()
|
||||
now = time.time()
|
||||
conn.executemany(
|
||||
|
||||
Reference in New Issue
Block a user