Code review fixes: wardrobe migration, response validation, path traversal guard, deduplication

- Migrate 11 character JSONs from old wardrobe keys to _BODY_GROUP_KEYS format
- Add is_favourite/is_nsfw columns to Preset model
- Add HTTP response validation and timeouts to ComfyUI client
- Add path traversal protection on replace cover route
- Deduplicate services/mcp.py (4 functions → 2 generic + 2 wrappers)
- Extract apply_library_filters() and clean_html_text() shared helpers
- Add named constants for 17 ComfyUI workflow node IDs
- Fix bare except clauses in services/llm.py
- Fix tags schema in ensure_default_outfit() (list → dict)
- Convert f-string logging to lazy % formatting
- Add 5-minute polling timeout to frontend waitForJob()
- Improve migration error handling (non-duplicate errors log at WARNING)
- Update CLAUDE.md to reflect all changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Aodhan Collins
2026-03-22 00:31:27 +00:00
parent 55ff58aba6
commit 29a6723b25
37 changed files with 464 additions and 539 deletions

View File

@@ -13,9 +13,9 @@ from services.job_queue import _enqueue_job, _make_finalize, _enqueue_task
from services.prompts import build_prompt, _resolve_character, _ensure_character_fields, _append_background
from services.sync import sync_outfits
from services.file_io import get_available_loras, _count_outfit_lora_assignments
from utils import allowed_file, _LORA_DEFAULTS
from utils import allowed_file, _LORA_DEFAULTS, clean_html_text
from services.llm import load_prompt, call_llm
from routes.shared import register_common_routes
from routes.shared import register_common_routes, apply_library_filters
logger = logging.getLogger('gaze')
@@ -25,18 +25,9 @@ def register_routes(app):
@app.route('/outfits')
def outfits_index():
query = Outfit.query
fav = request.args.get('favourite')
nsfw = request.args.get('nsfw', 'all')
if fav == 'on':
query = query.filter_by(is_favourite=True)
if nsfw == 'sfw':
query = query.filter_by(is_nsfw=False)
elif nsfw == 'nsfw':
query = query.filter_by(is_nsfw=True)
outfits = query.order_by(Outfit.is_favourite.desc(), Outfit.name).all()
outfits, fav, nsfw = apply_library_filters(Outfit.query, Outfit)
lora_assignments = _count_outfit_lora_assignments()
return render_template('outfits/index.html', outfits=outfits, lora_assignments=lora_assignments, favourite_filter=fav or '', nsfw_filter=nsfw)
return render_template('outfits/index.html', outfits=outfits, lora_assignments=lora_assignments, favourite_filter=fav, nsfw_filter=nsfw)
@app.route('/outfits/rescan', methods=['POST'])
def rescan_outfits():
@@ -90,11 +81,7 @@ def register_routes(app):
try:
with open(html_path, 'r', encoding='utf-8', errors='ignore') as hf:
html_raw = hf.read()
clean_html = re.sub(r'<script[^>]*>.*?</script>', '', html_raw, flags=re.DOTALL)
clean_html = re.sub(r'<style[^>]*>.*?</style>', '', clean_html, flags=re.DOTALL)
clean_html = re.sub(r'<img[^>]*>', '', clean_html)
clean_html = re.sub(r'<[^>]+>', ' ', clean_html)
html_content = ' '.join(clean_html.split())
html_content = clean_html_text(html_raw)
except Exception:
pass
@@ -313,12 +300,12 @@ def register_routes(app):
# No explicit field selection (e.g. batch generation) — build a selection
# that includes identity + wardrobe + name + lora triggers, but NOT character
# defaults (expression, pose, scene), so outfit covers stay generic.
from utils import _IDENTITY_KEYS, _WARDROBE_KEYS
for key in _IDENTITY_KEYS:
from utils import _BODY_GROUP_KEYS
for key in _BODY_GROUP_KEYS:
if character.data.get('identity', {}).get(key):
selected_fields.append(f'identity::{key}')
outfit_wardrobe = outfit.data.get('wardrobe', {})
for key in _WARDROBE_KEYS:
for key in _BODY_GROUP_KEYS:
if outfit_wardrobe.get(key):
selected_fields.append(f'wardrobe::{key}')
selected_fields.append('special::name')