Major refactor: deduplicate routes, sync, JS, and fix bugs
- Extract 8 common route patterns into factory functions in routes/shared.py (favourite, upload, replace cover, save defaults, clone, save JSON, get missing, clear covers) — removes ~1,100 lines across 9 route files - Extract generic _sync_category() in sync.py — 7 sync functions become one-liner wrappers, removing ~350 lines - Extract shared detail page JS into static/js/detail-common.js — all 9 detail templates now call initDetailPage() with minimal config - Extract layout inline JS into static/js/layout-utils.js (~185 lines) - Extract library toolbar JS into static/js/library-toolbar.js - Fix finalize missing-image bug: raise RuntimeError instead of logging warning so job is marked failed - Fix missing scheduler default in _default_checkpoint_data() - Fix N+1 query in Character.get_available_outfits() with batch IN query - Convert all print() to logger across services and routes - Add missing tags display to styles, scenes, detailers, checkpoints detail - Update delete buttons to use trash.png icon with solid red background - Update CLAUDE.md to reflect new architecture Net reduction: ~1,600 lines Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
122
app.py
122
app.py
@@ -66,38 +66,33 @@ if __name__ == '__main__':
|
||||
ensure_character_mcp_server_running()
|
||||
init_queue_worker(app)
|
||||
with app.app_context():
|
||||
from sqlalchemy import text
|
||||
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
db.create_all()
|
||||
|
||||
# Migration: Add active_outfit column if it doesn't exist
|
||||
try:
|
||||
from sqlalchemy import text
|
||||
db.session.execute(text('ALTER TABLE character ADD COLUMN active_outfit VARCHAR(100) DEFAULT \'default\''))
|
||||
db.session.commit()
|
||||
print("Added active_outfit column to character table")
|
||||
except Exception as e:
|
||||
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
|
||||
print("active_outfit column already exists")
|
||||
else:
|
||||
print(f"Migration note: {e}")
|
||||
# --- Helper for safe column additions ---
|
||||
def _add_column(table, column, col_type):
|
||||
try:
|
||||
db.session.execute(text(f'ALTER TABLE {table} ADD COLUMN {column} {col_type}'))
|
||||
db.session.commit()
|
||||
logger.info("Added %s.%s column", table, column)
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
if 'duplicate column name' not in str(e).lower() and 'already exists' not in str(e).lower():
|
||||
logger.debug("Migration note (%s.%s): %s", table, column, e)
|
||||
|
||||
# Migration: Add default_fields column to action table if it doesn't exist
|
||||
try:
|
||||
from sqlalchemy import text
|
||||
db.session.execute(text('ALTER TABLE action ADD COLUMN default_fields JSON'))
|
||||
db.session.commit()
|
||||
print("Added default_fields column to action table")
|
||||
except Exception as e:
|
||||
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
|
||||
print("default_fields column already exists in action table")
|
||||
else:
|
||||
print(f"Migration action note: {e}")
|
||||
# --- All migrations (grouped before syncs) ---
|
||||
_add_column('character', 'active_outfit', "VARCHAR(100) DEFAULT 'default'")
|
||||
_add_column('action', 'default_fields', 'JSON')
|
||||
_add_column('checkpoint', 'data', 'JSON')
|
||||
_add_column('look', 'character_ids', 'JSON')
|
||||
|
||||
# Migration: Add new columns to settings table
|
||||
columns_to_add = [
|
||||
('llm_provider', "VARCHAR(50) DEFAULT 'openrouter'"),
|
||||
('local_base_url', "VARCHAR(255)"),
|
||||
('local_model', "VARCHAR(100)"),
|
||||
# Settings columns
|
||||
for col_name, col_type in [
|
||||
('llm_provider', "VARCHAR(50) DEFAULT 'openrouter'"),
|
||||
('local_base_url', 'VARCHAR(255)'),
|
||||
('local_model', 'VARCHAR(100)'),
|
||||
('lora_dir_characters', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Looks'"),
|
||||
('lora_dir_outfits', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Clothing'"),
|
||||
('lora_dir_actions', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Poses'"),
|
||||
@@ -105,63 +100,33 @@ if __name__ == '__main__':
|
||||
('lora_dir_scenes', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Backgrounds'"),
|
||||
('lora_dir_detailers', "VARCHAR(500) DEFAULT '/ImageModels/lora/Illustrious/Detailers'"),
|
||||
('checkpoint_dirs', "VARCHAR(1000) DEFAULT '/ImageModels/Stable-diffusion/Illustrious,/ImageModels/Stable-diffusion/Noob'"),
|
||||
('default_checkpoint', "VARCHAR(500)"),
|
||||
('api_key', "VARCHAR(255)"),
|
||||
]
|
||||
for col_name, col_type in columns_to_add:
|
||||
try:
|
||||
db.session.execute(text(f'ALTER TABLE settings ADD COLUMN {col_name} {col_type}'))
|
||||
db.session.commit()
|
||||
print(f"Added {col_name} column to settings table")
|
||||
except Exception as e:
|
||||
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
|
||||
pass
|
||||
else:
|
||||
print(f"Migration settings note ({col_name}): {e}")
|
||||
('default_checkpoint', 'VARCHAR(500)'),
|
||||
('api_key', 'VARCHAR(255)'),
|
||||
]:
|
||||
_add_column('settings', col_name, col_type)
|
||||
|
||||
# Migration: Add is_favourite and is_nsfw columns to all resource tables
|
||||
_tag_tables = ['character', 'look', 'outfit', 'action', 'style', 'scene', 'detailer', 'checkpoint']
|
||||
for _tbl in _tag_tables:
|
||||
for _col, _type in [('is_favourite', 'BOOLEAN DEFAULT 0'), ('is_nsfw', 'BOOLEAN DEFAULT 0')]:
|
||||
try:
|
||||
db.session.execute(text(f'ALTER TABLE {_tbl} ADD COLUMN {_col} {_type}'))
|
||||
db.session.commit()
|
||||
print(f"Added {_col} column to {_tbl} table")
|
||||
except Exception as e:
|
||||
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
|
||||
pass
|
||||
else:
|
||||
print(f"Migration note ({_tbl}.{_col}): {e}")
|
||||
# is_favourite / is_nsfw on all resource tables
|
||||
for tbl in ['character', 'look', 'outfit', 'action', 'style', 'scene', 'detailer', 'checkpoint']:
|
||||
_add_column(tbl, 'is_favourite', 'BOOLEAN DEFAULT 0')
|
||||
_add_column(tbl, 'is_nsfw', 'BOOLEAN DEFAULT 0')
|
||||
|
||||
# Ensure settings exist
|
||||
if not Settings.query.first():
|
||||
db.session.add(Settings())
|
||||
db.session.commit()
|
||||
print("Created default settings")
|
||||
logger.info("Created default settings")
|
||||
|
||||
# Log the default checkpoint on startup
|
||||
# Log default checkpoint
|
||||
settings = Settings.query.first()
|
||||
if settings and settings.default_checkpoint:
|
||||
logger.info("=" * 80)
|
||||
logger.info("DEFAULT CHECKPOINT loaded from database: %s", settings.default_checkpoint)
|
||||
logger.info("=" * 80)
|
||||
logger.info("Default checkpoint: %s", settings.default_checkpoint)
|
||||
else:
|
||||
logger.info("No default checkpoint set in database")
|
||||
|
||||
# --- Sync all categories ---
|
||||
sync_characters()
|
||||
sync_outfits()
|
||||
sync_actions()
|
||||
# Migration: Add data column to checkpoint table
|
||||
try:
|
||||
db.session.execute(text('ALTER TABLE checkpoint ADD COLUMN data JSON'))
|
||||
db.session.commit()
|
||||
print("Added data column to checkpoint table")
|
||||
except Exception as e:
|
||||
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
|
||||
print("data column already exists in checkpoint table")
|
||||
else:
|
||||
print(f"Migration checkpoint note: {e}")
|
||||
|
||||
sync_styles()
|
||||
sync_detailers()
|
||||
sync_scenes()
|
||||
@@ -169,20 +134,7 @@ if __name__ == '__main__':
|
||||
sync_checkpoints()
|
||||
sync_presets()
|
||||
|
||||
# Migration: Convert look.character_id to look.character_ids
|
||||
try:
|
||||
from sqlalchemy import text
|
||||
# First ensure the column exists
|
||||
db.session.execute(text("ALTER TABLE look ADD COLUMN character_ids JSON"))
|
||||
db.session.commit()
|
||||
print("Added character_ids column to look table")
|
||||
except Exception as e:
|
||||
if 'duplicate column name' in str(e).lower() or 'already exists' in str(e).lower():
|
||||
pass # Column already exists
|
||||
else:
|
||||
print(f"Migration note (character_ids column): {e}")
|
||||
|
||||
# Migrate existing character_id to character_ids list
|
||||
# --- Post-sync data migration: character_id → character_ids ---
|
||||
try:
|
||||
looks_with_old_field = Look.query.filter(Look.character_id.isnot(None)).all()
|
||||
migrated_count = 0
|
||||
@@ -194,8 +146,8 @@ if __name__ == '__main__':
|
||||
migrated_count += 1
|
||||
if migrated_count > 0:
|
||||
db.session.commit()
|
||||
print(f"Migrated {migrated_count} looks from character_id to character_ids")
|
||||
logger.info("Migrated %d looks from character_id to character_ids", migrated_count)
|
||||
except Exception as e:
|
||||
print(f"Migration note (character_ids data): {e}")
|
||||
logger.debug("Migration note (character_ids data): %s", e)
|
||||
|
||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||
|
||||
Reference in New Issue
Block a user