- state.json: Phase 5 status changed to pending, next steps updated - DEVELOPMENT_PLAN.md v1.1: Added actual progress, verified current state, renumbered phases 5-9, marked all remaining work as PENDING - Deleted outdated plans (BACKEND_TEST_PLAN, DATA_LOADING_VERIFICATION, DOCKER_MIGRATION_PLAN, ENDPOINT_AUDIT, ROADMAP, SIMPLIFICATION_PLAN, STATEMENT_OF_INTENT, backend/TEST_PLAN) - Added new files: DEVELOPMENT_PLAN.md, backend/ARCHITECTURAL_REPORT.md, backend/SIMPLIFICATION_SUMMARY.md, backend/run.py, backend/.dockerignore
51 KiB
MTG Online Backend - Comprehensive Architectural Report
Project: MTG Online Backend
Location: /home/wall-o/projects/mtgonline/backend/app/
Framework: FastAPI with SQLAlchemy async
Database: SQLite (dev) / PostgreSQL (prod) + Redis caching
Date: 2025-07-21
Executive Summary
The MTG Online backend is a FastAPI application providing card game management features including user authentication, deck building, card search with fuzzy matching, card collection management, and real-time game WebSocket support. The codebase exhibits significant architectural debt with multiple layers of duplicated functionality (old vs. new router patterns), inconsistent database models, and redundant services.
Key Issues Identified:
- Router Duplication: Every major feature has both old and new router implementations
- Model Inconsistency: Multiple model files with overlapping functionality
- Service Redundancy: Multiple services performing similar operations
- Schema Confusion: Duplicate schema definitions across files
- Mixed Database Patterns: Some services use ORM, others use raw SQL
Layer Analysis
1. Core Layer (app/core/)
Purpose: Application infrastructure - configuration, database, security, logging, caching
1.1 config.py (216 lines)
Purpose: Application configuration management with environment variable overrides
Key Classes/Functions:
Configclass: Configuration with sensible defaultsget_config(): Singleton configuration instance_load_env_file(): Load .env files
Imports From:
os(stdlib)typing.Optional
Imported By:
database.pysecurity.pysettings.py(via config)
Dependencies: None (standalone)
1.2 database.py (193 lines)
Purpose: SQLAlchemy async engine and session management
Key Classes/Functions:
Base: DeclarativeBase for ORM modelsengine: Async engine instanceasync_session: Session factoryget_db(): FastAPI dependency for database sessionscreate_tables(): Create all tables- Backward-compatible aliases:
mtg_engine,mtg_async_session,mtg_get_db,mirror_get_db
Imports From:
app.core.config:get_configsqlalchemy.ext.asyncio:AsyncSession,async_sessionmaker,create_async_enginesqlalchemy.orm:DeclarativeBase
Imported By:
models/__init__.pymodels/models.pymodels/mtg_models.pyrouters/*.py(multiple)services/*.py(multiple)
Dependencies: config.py
1.3 logging.py (187 lines)
Purpose: Structured logging configuration with JSON/text support
Key Classes/Functions:
ContainerFormatter: Custom log formatterconfigure_logging(): Configure application logging
Imports From:
json,logging,os,sys,datetime,typing
Imported By:
- Imported by
logging_config.py(duplicate) - Used by various services
Dependencies: None (standalone)
1.4 logging_config.py (97 lines)
Purpose: DUPLICATE of logging.py - structured logging configuration
Key Classes/Functions:
ContainerFormatter: Same as logging.pyconfigure_logging(): Same as logging.py
Imports From:
json,logging,os,sys,datetime
Imported By:
- Auto-configures on import
Dependencies: None (standalone)
⚠️ ISSUE: Exact duplicate of logging.py. Should be removed.
1.5 redis_client.py (90 lines)
Purpose: Redis client and caching layer
Key Classes/Functions:
get_redis(): Get Redis client instanceclose_redis(): Close Redis connectioncache_get(),cache_set(),cache_delete(),cache_invalidate_pattern(): Cache operations
Imports From:
app.core.settings:get_settingsredis.asyncio:aioredis
Imported By:
routers/card_router.pyrouters/cards_router.pyrouters/interactions.py
Dependencies: settings.py
1.6 security.py (238 lines)
Purpose: Authentication and password hashing utilities
Key Classes/Functions:
verify_password(),hash_password(): Password operationscreate_access_token(),create_refresh_token(): JWT token creationdecode_token(): JWT token validationget_current_user(): FastAPI dependency for auth
Imports From:
app.core.config:get_configfastapi:Header,HTTPException,statusjose:JWTError,jwtpasslib.context:CryptContext
Imported By:
routers/auth.pyrouters/auth_router.pyrouters/users.pyrouters/users_router.pyrouters/admin.pyrouters/rooms.pyrouters/games_router.py
Dependencies: config.py
1.7 settings.py (29 lines)
Purpose: BACKWARD COMPATIBILITY SHIM - re-exports config module
Key Classes/Functions:
get_settings: Alias forget_config
Imports From:
app.core.config:Config,get_config
Imported By:
redis_client.pyservices/mtgjson_loader.pyservices/mtgjson_uploader.py
Dependencies: config.py
⚠️ ISSUE: Should be removed once all callers migrate to config.py
2. Models Layer (app/models/)
Purpose: SQLAlchemy ORM models for database entities
2.1 __init__.py (30 lines)
Purpose: Exports all models from models.py and mtg_models.py
Imports From:
app.models.models: All application modelsapp.models.mtg_models: MtgSet, MtgCard, MtgCardMirror, DeckCardLink
Imported By:
- Various routers and services
Dependencies: models.py, mtg_models.py
2.2 models.py (968 lines)
Purpose: Main application ORM models
Key Classes:
- Core:
User,DecklistFolder,DecklistFile,Room,RoomGameType,Ban,GameLog,AuditLog - Card Mirror:
MtgonlineCard(local copy for deckbuilding) - User Decks:
UserDeck,UserDeckCard,DeckPrecedent,DeckPrecedentCard,CardSuggestion - User Data:
UserSession,DeckVersion,GameReplay,ReplayPlayer,GameOutcome,UserStatistics,UserCardCollection,CardWishlist,UserGroup,GroupMember,GroupChatMessage,UserNetwork,NetworkMember,UserPreference,UserActivityLog - Card Imports:
CardImportBatch,UserCardImportRecord,UserCardImport
Imports From:
app.core.database:Basesqlalchemy: Various column types, relationships
Imported By:
models/__init__.pyrouters/*.py(multiple)services/*.py(multiple)
Dependencies: database.py
2.3 mtg_models.py (184 lines)
Purpose: MTG card data models
Key Classes:
MtgSet: MTG set dataMtgCard: MTG card data (canonical)MtgCardMirror: Mirrored card data for fast queriesDeckCardLink: Junction table linking decks to mirrored cards
Imports From:
app.core.database:Basesqlalchemy: Column types, relationships
Imported By:
models/__init__.pyrouters/card_router.pyrouters/cards_router.pyservices/card_search_service.pyservices/deck_suggestion_service.py
Dependencies: database.py
2.4 __backup__/ (Directory)
Purpose: Backup of old model files (not analyzed)
3. Routers Layer (app/routers/)
Purpose: FastAPI route handlers for API endpoints
⚠️ CRITICAL ISSUE: Every major feature has both old and new router implementations with significant duplication.
3.1 __init__.py (29 lines)
Purpose: Exports consolidated routers
Imports From:
app.routers.auth_routerapp.routers.users_routerapp.routers.decks_routerapp.routers.cards_routerapp.routers.user_data_routerapp.routers.games_router
Imported By:
app/main.py
Dependencies: All router modules
3.2 auth.py (162 lines) - OLD VERSION
Purpose: Authentication endpoints (login, refresh, register, me)
Key Endpoints:
POST /login: Authenticate userPOST /refresh: Refresh access tokenPOST /register: Register new userGET /me: Get current user
Imports From:
app.core.database:get_dbapp.core.security:verify_password,hash_password,create_access_token,create_refresh_token,decode_tokenapp.models.models:Userapp.schemas.schemas: Login/refresh/user schemas
Imported By: None (old version, superseded by auth_router.py)
Dependencies: database.py, security.py, models.py, schemas.py
3.3 auth_router.py (246 lines) - NEW VERSION
Purpose: Authentication endpoints (consolidated)
Key Endpoints: Same as auth.py but with better structure
Imports From:
app.core.database:get_dbapp.core.security: Token functionsapp.models.models:Userapp.schemas.schemas: Login/refresh/user schemas
Imported By:
app/main.py
Dependencies: database.py, security.py, models.py, schemas.py
3.4 users.py (168 lines) - OLD VERSION
Purpose: User management endpoints
Key Endpoints:
GET /{user_id}: Get userPATCH /{user_id}: Update userPOST /{user_id}/ban: Ban userPOST /{user_id}/unban: Unban user
Imports From:
app.core.database:get_dbapp.core.security:get_current_user,hash_passwordapp.models.models:Userapp.schemas.schemas: User schemas
Imported By: None (old version, superseded by users_router.py)
Dependencies: database.py, security.py, models.py, schemas.py
3.5 users_router.py (549 lines) - NEW VERSION
Purpose: User management + admin operations (consolidated)
Key Endpoints:
- User management: GET/PATCH user, ban/unban
- Admin operations: List users, list bans, create ban, unban by ID, list logs, audit log
Imports From:
app.core.database:get_dbapp.core.security:get_current_user,hash_passwordapp.models.models:User,Ban,GameLog,AuditLogapp.schemas.schemas: User/ban schemas
Imported By:
app/main.py
Dependencies: database.py, security.py, models.py, schemas.py
3.6 decks.py (721 lines) - OLD VERSION
Purpose: Deck management endpoints
Key Endpoints:
- CRUD for user decks
- Deck card management
- Precedent management
- Card search integration
- Card suggestions
Imports From:
app.core.database:get_db,mtg_get_dbapp.core.security:get_current_userapp.models.models:User,DecklistFolder,MtgonlineCardapp.models.user_deck:UserDeck,UserDeckCard, etc.app.services.deck_manager:DeckManagerapp.services.deck_parser:DeckParser
Imported By: None (old version, superseded by decks_router.py)
Dependencies: Multiple
3.7 decks_router.py (810 lines) - NEW VERSION
Purpose: Deck management (consolidated)
Key Endpoints: Same as decks.py but with better structure
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.mtg_models:MtgCard,MtgSetapp.models.models:MtgonlineCard,User,DecklistFolderapp.models.user_deck: Deck modelsapp.services.deck_manager:DeckManagerapp.services.deck_parser:DeckParser
Imported By:
app/main.py
Dependencies: Multiple
3.8 card_router.py (292 lines) - OLD VERSION
Purpose: Card search endpoints
Key Endpoints:
GET /api/cards/search: Search cardsGET /api/cards/{card_id}: Get card by IDGET /api/cards/sets: List setsGET /api/cards/types: List card typesGET /api/cards/rarities: List raritiesGET /api/cards/suggest: Suggest cards
Imports From:
app.core.database:mtg_get_dbapp.core.redis_client:cache_get,cache_setapp.services.card_search_service:CardSearchServiceapp.services.deck_suggestion_service:DeckSuggestionServiceapp.services.fuzzy_card_matcher:FuzzyCardMatcherapp.models.user_deck:UserDeckapp.models.mtg_models:MtgCard
Imported By: None (old version, superseded by cards_router.py)
Dependencies: Multiple
3.9 cards_router.py (995 lines) - NEW VERSION
Purpose: Card search + card interactions (consolidated)
Key Endpoints:
- Card search: Same as card_router.py
- Card interactions: synergies, counters, evolutions, recommendations, stats
Imports From:
app.core.database:mtg_get_dbapp.core.redis_client:cache_get,cache_setapp.models.mtg_models:MtgCardapp.models.user_deck:UserDeckapp.schemas.card_search_schemas: Card schemasapp.services.card_search_service:CardSearchServiceapp.services.deck_suggestion_service:DeckSuggestionServiceapp.services.fuzzy_card_matcher:FuzzyCardMatcher
Imported By:
app/main.py
Dependencies: Multiple
3.10 interactions.py (674 lines) - OLD VERSION
Purpose: Card interaction endpoints (synergies, counters, evolutions)
Key Endpoints:
GET /interactions/synergies/{card_id}GET /interactions/counters/{card_id}GET /interactions/evolutions/{card_id}GET /interactions/recommend/{card_id}- Search endpoints for each interaction type
GET /interactions/stats/{card_id}
Imports From:
app.core.database:mtg_get_dbapp.core.redis_client:cache_get,cache_set
Imported By: None (old version, superseded by cards_router.py)
Dependencies: database.py, redis_client.py
3.11 user_data.py (1714 lines) - OLD VERSION
Purpose: User data endpoints (sessions, decks, replays, collections, groups, networks, preferences)
Key Endpoints:
- Session management
- Deck versions
- Game replays
- Card collection
- Wishlist
- Groups with chat
- Networks
- Preferences
- Activity log
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.models:Userapp.models.user_data: Various user data modelsapp.schemas.user_card_collection: Collection schemasapp.schemas.user_data_schemas: User data schemas
Imported By: None (old version, superseded by user_data_router.py)
Dependencies: Multiple
3.12 user_data_router.py (69121 bytes) - NEW VERSION
Purpose: User data + card import (consolidated)
Key Endpoints:
- All user_data.py endpoints
- Card import endpoints (upload, status, results, confirm)
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.models:User,MtgonlineCardapp.models.user_data: User data modelsapp.models.card_import_batch:CardImportBatchapp.models.user_card_import_record:UserCardImportRecordapp.models.user_deck:UserDeck,UserDeckCardapp.services.file_parser:FileParserapp.services.import_batch_processor:ImportBatchProcessorapp.services.fuzzy_card_matcher:FuzzyCardMatcherapp.schemas.*: Various schemas
Imported By:
app/main.py
Dependencies: Multiple
3.13 rooms.py (158 lines) - OLD VERSION
Purpose: Room management endpoints
Key Endpoints:
GET /: List roomsGET /{room_id}: Get roomPOST /: Create room (admin)PATCH /{room_id}: Update room (admin)DELETE /{room_id}: Delete room (admin)
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.models:Roomapp.schemas.schemas:RoomResponse
Imported By: None (old version, superseded by games_router.py)
Dependencies: database.py, security.py, models.py, schemas.py
3.14 games/__init__.py (4 lines)
Purpose: Games router package init
Imports From:
app.routers.games.router:router
Imported By: None
Dependencies: games/router.py
3.15 games/router.py (135 lines) - OLD VERSION
Purpose: Game management stubs
Key Endpoints:
GET /: List games (stub)POST /: Create game (mock)GET /{game_id}: Get game (mock)POST /{game_id}/join: Join game (stub)POST /{game_id}/leave: Leave game (stub)POST /{game_id}/start: Start game (stub)POST /{game_id}/end: End game (stub)
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.models:Userapp.schemas.schemas:GameCreate,GameResponse
Imported By: None (old version, superseded by games_router.py)
Dependencies: database.py, security.py, models.py, schemas.py
3.16 games_router.py (487 lines) - NEW VERSION
Purpose: Room management + game stubs + MTGJSON refresh + WebSocket (consolidated)
Key Endpoints:
- Room CRUD (prefix: /rooms)
- Game stubs (prefix: /games)
- MTGJSON refresh (prefix: /mtgjson)
- WebSocket (prefix: /ws/game/{game_id})
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.models:Room,Userapp.schemas.schemas:RoomResponse,GameCreate,GameResponseapp.services.mtgjson_manager:MTGJSONManagerapp.services.game_server:game_websocket_endpoint
Imported By:
app/main.py
Dependencies: Multiple
3.17 refresh.py (101 lines) - OLD VERSION
Purpose: MTGJSON refresh endpoints
Key Endpoints:
POST /mtgjson/refresh: Trigger refreshGET /mtgjson/status: Get statusPOST /mtgjson/verify: Verify files
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.services.mtgjson_manager:MTGJSONManager
Imported By: None (old version, superseded by games_router.py)
Dependencies: database.py, security.py, services/mtgjson_manager.py
3.18 card_import.py (368 lines) - OLD VERSION
Purpose: Card import endpoints
Key Endpoints:
POST /import: Upload import fileGET /import/{import_id}/status: Get statusGET /import/{import_id}/results: Get resultsPOST /import/{import_id}/confirm: Confirm importGET /user/cards: List user cardsDELETE /user/cards/{card_import_id}: Delete cardGET /user/decks: List user decks
Imports From:
app.core.database:get_dbapp.core.security:get_current_userapp.models.card_import_batch:CardImportBatchapp.models.user_card_import_record:UserCardImportRecordapp.models.user_data:UserCardCollectionapp.models.models:MtgonlineCardapp.models.user_deck:UserDeck,UserDeckCardapp.services.file_parser:FileParserapp.services.import_batch_processor:ImportBatchProcessorapp.services.fuzzy_card_matcher:FuzzyCardMatcher
Imported By: None (old version, superseded by user_data_router.py)
Dependencies: Multiple
3.19 ws.py (512 bytes) - OLD VERSION
Purpose: WebSocket endpoint for game connections
Key Endpoints:
GET /ws/game/{game_id}: WebSocket connection
Imports From:
app.services.game_server:game_websocket_endpoint
Imported By: None (old version, superseded by games_router.py)
Dependencies: services/game_server.py
4. Schemas Layer (app/schemas/)
Purpose: Pydantic models for request/response validation
4.1 __init__.py (147 lines)
Purpose: Exports all schemas from various schema files
Imports From:
app.schemas.schemas: Basic schemasapp.schemas.user_data_schemas: User data schemasapp.schemas.user_card_collection: Collection schemasapp.schemas.user_deck_schemas: Deck schemasapp.schemas.card_import_schemas: Import schemasapp.schemas.card_search_schemas: Search schemasapp.schemas.game_schemas: Game schemasapp.schemas.mtg_card_schemas: MTG card schemasapp.schemas.proto_messages: Protocol messagesapp.schemas.protocol_constants: Protocol constants
Imported By:
- Various routers
Dependencies: All schema files
4.2 schemas.py (286 lines)
Purpose: Basic Pydantic models for auth, users, decks, games, bans
Key Classes:
- Auth:
LoginRequest,LoginResponse,RefreshTokenRequest,TokenResponse - User:
UserBase,UserCreate,UserUpdate,UserResponse - Deck:
DeckCreate,DeckUpdate,DeckResponse,FolderCreate,FolderResponse - Game:
GameCreate,GameResponse - Room:
RoomResponse - Ban:
BanCreate,BanResponse - Error:
ErrorResponse,ValidationErrorResponse - Pagination:
PaginationParams,PaginatedResponse - Card Mirror:
CardMirrorResponse,DeckCardLinkResponse,DeckWithCardsResponse
Imports From:
pydantic:BaseModel,EmailStr,Field,ConfigDicttyping:Optional,List,Dictdatetime:datetime
Imported By:
routers/auth.pyrouters/auth_router.pyrouters/users.pyrouters/users_router.pyrouters/rooms.pyrouters/games_router.py
Dependencies: None (standalone)
4.3 user_data_schemas.py (434 lines)
Purpose: User data schemas (sessions, decks, replays, collections, groups, networks, preferences)
Key Classes:
- Enums:
DeckVersionStatus,GameReplayStatus,GameOutcomeType,GroupMemberRole,NetworkMemberRole,UserPreferenceTheme,ActivityType - Session:
SessionResponse,SessionCleanupResponse - Deck Version:
DeckVersionCreate,DeckVersionUpdate,DeckVersionResponse,DeckVersionListResponse - Game Replay:
GameReplayCreate,GameReplayUpdate,GameReplayResponse,GameReplayListResponse - Game Outcome:
GameOutcomeCreate,GameOutcomeResponse,GameOutcomeListResponse - User Statistics:
UserStatisticsResponse,StatisticsUpdateResponse - Group:
GroupCreate,GroupUpdate,GroupMemberCreate,GroupMemberUpdate,GroupMemberRemove,GroupResponse,GroupListResponse,GroupChatMessageCreate,GroupChatMessageResponse,GroupChatMessageListResponse - Network:
NetworkCreate,NetworkUpdate,NetworkMemberCreate,NetworkResponse,NetworkListResponse - Preference:
UserPreferenceUpdate,UserPreferenceResponse - Activity Log:
ActivityLogEntry,ActivityLogListResponse - Generic:
MessageResponse,CountResponse,ErrorDetail
Imports From:
pydantic:BaseModel,Field,ConfigDicttyping:Optional,List,Dict,Anydatetime:datetimeenum:Enum
Imported By:
routers/user_data.pyrouters/user_data_router.py
Dependencies: None (standalone)
4.4 user_deck_schemas.py (217 lines)
Purpose: Deck building schemas
Key Classes:
- Enums:
DeckStatus,DeckZone,SuggestionType - Deck:
UserDeckCreate,UserDeckUpdate,UserDeckResponse,UserDeckListResponse - Deck Card:
DeckCardCreate,DeckCardUpdate,DeckCardResponse,DeckCardWithDetailsResponse,DeckCardListResponse - Precedent:
PrecedentCreate,PrecedentUpdate,PrecedentResponse,PrecedentListResponse - Suggestion:
SuggestionCreate,SuggestionResponse,SuggestionListResponse - Actions:
DeckFinalizeRequest,DeckFinalizeResponse,DeckDeleteResponse - Search:
CardSearchRequest
Imports From:
pydantic:BaseModel,Field,ConfigDicttyping:Optional,List,Dict,Anydatetime:datetimeenum:Enum
Imported By:
routers/decks.pyrouters/decks_router.pyrouters/card_import.py
Dependencies: None (standalone)
4.5 user_card_collection.py (157 lines)
Purpose: Card collection schemas
Key Classes:
- Enums:
CardCondition,AcquisitionMethod - Collection:
CardCollectionCreate,CardCollectionUpdate,CardCollectionResponse,CardCollectionListResponse - Wishlist:
WishlistCreate,WishlistUpdate,WishlistResponse,WishlistListResponse - Statistics:
CollectionStatistics,CollectionSummaryResponse - Generic:
MessageResponse,CountResponse,ErrorDetail
Imports From:
pydantic:BaseModel,Field,ConfigDicttyping:Optional,List,Dict,Anydatetime:datetimeenum:Enum
Imported By:
routers/user_data.pyrouters/user_data_router.py
Dependencies: None (standalone)
4.6 card_import_schemas.py (113 lines)
Purpose: Card import schemas
Key Classes:
- Import:
CardImportRequest,CardImportResponse,CardImportStatusResponse - Match:
CardMatchResult,CardImportSummary - Batch:
CardImportBatchCreate,CardImportBatchResponse - User Import:
UserCardImportCreate,UserCardImportResponse,UserCardImportRecordResponse
Imports From:
pydantic:BaseModel,Field,ConfigDicttyping:List,Optional,Dict,Anydatetime:datetime
Imported By:
routers/card_import.pyrouters/user_data_router.py
Dependencies: None (standalone)
4.7 card_search_schemas.py (108 lines)
Purpose: Card search and import schemas
Key Classes:
- Search:
CardResponse,SetResponse,CardTypeResponse,CardSearchResponse - Import:
CardImportResponse,CardImportStatusResponse,CardMatchResult,CardImportSummary - Generic:
MessageResponse,CountResponse,ErrorResponse
Imports From:
pydantic:BaseModel,Field,ConfigDicttyping:List,Optional,Dict,Anydatetime:datetime
Imported By:
routers/card_router.pyrouters/cards_router.py
Dependencies: None (standalone)
4.8 mtg_card_schemas.py (116 lines)
Purpose: MTG card schemas
Key Classes:
- Card:
MtgCardResponse,MtgCardSearchRequest,MtgCardSearchResponse - Set:
MtgSetResponse - Mirror:
MtgCardMirrorResponse - Link:
DeckCardLinkResponse,DeckWithCardsResponse
Imports From:
pydantic:BaseModel,ConfigDicttyping:Optional,Listdatetime:datetime
Imported By:
routers/cards_router.py
Dependencies: None (standalone)
4.9 game_schemas.py (59 lines)
Purpose: Game schemas
Key Classes:
- Game:
GameCreate,GameResponse,GameJoinRequest,GameLeaveRequest,GameListResponse,GamePlayerResponse,GameStateResponse
Imports From:
pydantic:BaseModel,Field,ConfigDicttyping:Optional,Listdatetime:datetime
Imported By:
routers/games_router.py
Dependencies: None (standalone)
4.10 generic_schemas.py (29 lines)
Purpose: Generic response schemas
Key Classes:
MessageResponse,CountResponse,ErrorResponse,ValidationErrorResponse,ErrorDetail
Imports From:
pydantic:BaseModeltyping:Optional,List
Imported By:
routers/user_data.pyrouters/user_data_router.py
Dependencies: None (standalone)
4.11 proto_messages.py (133 lines)
Purpose: Protocol buffer message models
Key Classes:
- Base:
ProtoMessageBase - Commands:
SessionCommand,GameCommand,GameEvent,Response - Server Info:
ServerInfoUser,ServerInfoDeckStorageFile,ServerInfoDeckStorageFolder,ServerInfoDeckStorageTreeItem,ServerInfoCard,ServerInfoZone,ServerInfoGame
Imports From:
pydantic:BaseModeltyping:Optional,List,Dict,Anydatetime:datetime
Imported By:
- None (unused)
Dependencies: None (standalone)
⚠️ ISSUE: Protocol buffer messages defined but not used anywhere.
4.12 protocol_constants.py (170 lines)
Purpose: MTG Online protocol constants
Key Classes:
SessionCommandType: Session command types (PING, LOGIN, MESSAGE, etc.)GameCommandType: Game command types (KICK, LEAVE, SHUFFLE, etc.)GameEventType: Game event types (JOIN, LEAVE, DRAW_CARDS, etc.)ResponseCode: Response codes (RespOk, RespInvalidCommand, etc.)ZoneType: Zone types (PrivateZone, PublicZone, HiddenZone)UserLevelFlag: User level flags (IsUser, IsModerator, IsAdmin, etc.)
Imports From:
enum:IntEnum,IntFlag
Imported By:
- None (unused)
Dependencies: None (standalone)
⚠️ ISSUE: Protocol constants defined but not used anywhere.
5. Services Layer (app/services/)
Purpose: Business logic and data processing
5.1 __init__.py (60 lines)
Purpose: Exports all services
Imports From:
app.services.deck_parser:DeckParserapp.services.card_database: Various card database functionsapp.services.card_mirror_service: Mirror service functionsapp.services.mtgjson_manager:MTGJSONManager,get_managerapp.services.mtgjson_downloader: Download functionsapp.services.mtgjson_loader: Loader functionsapp.services.mtgjson_uploader: Uploader functionsapp.services.file_parser:FileParserapp.services.fuzzy_card_matcher:FuzzyCardMatcherapp.services.import_batch_processor:ImportBatchProcessorapp.services.deck_manager:DeckManagerapp.services.card_search_service:CardSearchServiceapp.services.deck_suggestion_service:DeckSuggestionService
Imported By:
- Various routers
Dependencies: All service modules
5.2 card_database.py (350 lines)
Purpose: MTG card database queries
Key Functions:
search_cards(): Search cards by name/type/mana costget_card_by_name(): Get card by nameget_cards_by_set(): Get cards by setget_card_types(): Get all card typesget_card_rarities(): Get all raritiesget_sets(): Get all setsget_set_by_code(): Get set by codeget_card_statistics(): Get database statistics
Imports From:
app.models.mtg_models:MtgCard,MtgSetapp.core.database:mtg_get_dbsqlalchemy:select,and_,or_,funcsqlalchemy.orm:selectinload
Imported By:
routers/card_router.py
Dependencies: models/mtg_models.py, core/database.py
5.3 card_mirror_service.py (359 lines)
Purpose: Card mirror service for fast deckbuilding queries
Key Functions:
upsert_card_mirror(): Upsert card to mirrorget_card_mirror_by_name(): Get mirror by namesearch_card_mirrors(): Search mirrorsadd_card_to_deck(): Add card to deckremove_card_from_deck(): Remove card from deckget_deck_cards(): Get deck cardsget_user_deck_summaries(): Get user deck summariessync_mirrors_from_mtg_cards(): Sync mirrorsget_card_statistics(): Get mirror statistics
Imports From:
app.models.mtg_models:MtgCard,MtgSetapp.models.mirror_models:MtgCardMirror,DeckCardLinkapp.core.database:mirror_get_dbsqlalchemy:select,func,update,deletesqlalchemy.orm:selectinload
Imported By:
- None (unused)
Dependencies: models/mtg_models.py, core/database.py
⚠️ ISSUE: Service defined but not used anywhere.
5.4 card_search_service.py (190 lines)
Purpose: Card search with filters
Key Classes/Functions:
CardSearchService.search_cards(): Search with filtersCardSearchService.get_card_by_id(): Get by IDCardSearchService.get_sets(): Get all setsCardSearchService.get_card_types(): Get card typesCardSearchService.get_card_rarities(): Get rarities
Imports From:
app.models.mtg_models:MtgCard,MtgSetapp.models.mirror_models:MtgCardMirrorsqlalchemy:select,or_,and_sqlalchemy.orm:selectinload
Imported By:
routers/card_router.pyrouters/cards_router.py
Dependencies: models/mtg_models.py, sqlalchemy
5.5 deck_manager.py (246 lines)
Purpose: Deck CRUD operations
Key Classes/Functions:
DeckManager.create_deck(): Create deckDeckManager.get_deck(): Get deckDeckManager.list_decks(): List decksDeckManager.update_deck(): Update deckDeckManager.delete_deck(): Delete deckDeckManager.finalize_deck(): Finalize deckDeckManager.add_card_to_deck(): Add cardDeckManager.get_deck_cards(): Get deck cardsDeckManager.update_deck_card(): Update cardDeckManager.remove_card_from_deck(): Remove cardDeckManager.clone_precedent(): Clone precedent
Imports From:
app.models.user_deck:UserDeck,UserDeckCard,DeckPrecedent,DeckPrecedentCardapp.models.models:MtgonlineCardsqlalchemy:select,update,deletesqlalchemy.orm:selectinload
Imported By:
routers/decks.pyrouters/decks_router.py
Dependencies: models/user_deck.py, models/models.py, sqlalchemy
5.6 deck_parser.py (206 lines)
Purpose: Deck list parsing and serialization
Key Classes/Functions:
CardInfo: Dataclass for card informationDeckParser.parse_plain_text(): Parse plain textDeckParser.format_plain_text(): Format as plain textDeckParser.to_native_xml(): Convert to XMLDeckParser.from_native_xml(): Parse XMLparse_deck(): Convenience functionformat_deck(): Convenience function
Imports From:
re: Regex patternstyping:List,Dict,Tuple,Optionaldataclasses:dataclass
Imported By:
routers/decks.pyrouters/decks_router.py
Dependencies: None (standalone)
5.7 deck_suggestion_service.py (209 lines)
Purpose: Card suggestion service
Key Classes/Functions:
DeckSuggestionService.suggest_cards(): Suggest cardsDeckSuggestionService.add_suggestion(): Add suggestionDeckSuggestionService.get_deck_suggestions(): Get suggestions
Imports From:
app.models.user_deck:UserDeck,UserDeckCard,CardSuggestionapp.models.mtg_models:MtgCardapp.models.mirror_models:MtgCardMirrorapp.services.fuzzy_card_matcher:FuzzyCardMatchersqlalchemy:select,or_,and_,func,casesqlalchemy.orm:selectinload
Imported By:
routers/card_router.pyrouters/cards_router.py
Dependencies: models/user_deck.py, models/mtg_models.py, services/fuzzy_card_matcher.py, sqlalchemy
5.8 file_parser.py (108 lines)
Purpose: File parser for card imports
Key Classes/Functions:
FileParser.parse_file(): Parse file (CSV, JSON, XLSX, ODS)FileParser._parse_csv(): Parse CSVFileParser._parse_json(): Parse JSONFileParser._parse_xlsx(): Parse XLSXFileParser._parse_ods(): Parse ODS
Imports From:
csv,json: Stdlibpathlib:Pathopenpyxl: XLSX parsingpandas: ODS parsing
Imported By:
routers/card_import.pyrouters/user_data_router.py
Dependencies: openpyxl, pandas
5.9 fuzzy_card_matcher.py (170 lines)
Purpose: Fuzzy matching for card names
Key Classes/Functions:
FuzzyCardMatcher.normalize_card_name(): Normalize nameFuzzyCardMatcher.exact_match(): Exact matchFuzzyCardMatcher.fuzzy_match(): Fuzzy matchFuzzyCardMatcher.find_best_match(): Find best matchFuzzyCardMatcher.batch_match(): Batch matchFuzzyCardMatcher.batch_match_with_database(): Match with DB
Imports From:
thefuzz:fuzztyping:List,Tuple,Optional
Imported By:
routers/card_router.pyrouters/cards_router.pyrouters/card_import.pyrouters/user_data_router.pyservices/deck_suggestion_service.pyservices/import_batch_processor.py
Dependencies: thefuzz
5.10 import_batch_processor.py (204 lines)
Purpose: Batch import processing
Key Classes/Functions:
ImportBatchProcessor.create_batch(): Create batchImportBatchProcessor.process_batch(): Process batchImportBatchProcessor.get_batch_status(): Get statusImportBatchProcessor.get_batch_results(): Get resultsImportBatchProcessor.confirm_batch(): Confirm batchImportBatchProcessor.get_user_imports(): Get user importsImportBatchProcessor.delete_batch(): Delete batch
Imports From:
app.models.card_import_batch:CardImportBatchapp.models.user_card_import_record:UserCardImportRecordapp.models.user_data:UserCardCollectionapp.models.models:MtgonlineCardapp.services.fuzzy_card_matcher:FuzzyCardMatchersqlalchemy:select,update,insert,deletesqlalchemy.sql:func
Imported By:
routers/card_import.pyrouters/user_data_router.py
Dependencies: models/card_import_batch.py, models/user_card_import_record.py, models/user_data.py, models/models.py, services/fuzzy_card_matcher.py, sqlalchemy
5.11 game_server.py (309 lines)
Purpose: WebSocket game server for real-time gameplay
Key Classes/Functions:
GameRoom: Manages single game roomGameServer: Manages all game roomsgame_websocket_endpoint(): WebSocket endpointprocess_game_command(): Process commands
Imports From:
asyncio:Lockjson: Serializationtyping:Dict,Set,Optionaldatetime:datetimefastapi:WebSocket,WebSocketDisconnect
Imported By:
routers/games_router.pyrouters/ws.py
Dependencies: fastapi
5.12 mtgjson_downloader.py (137 lines)
Purpose: MTGJSON data downloader
Key Functions:
get_downloads_dir(): Get downloads directorydownload_file(): Download single filedownload_all_files(): Download all filesverify_downloads(): Verify downloadsget_file_list(): Get file list
Imports From:
gzip,json,os,sys: Stdlibpathlib:Pathurllib.request:urlretrieveurllib.parse:urljoin
Imported By:
services/__init__.py
Dependencies: None (standalone)
5.13 mtgjson_loader.py (838 lines)
Purpose: MTGJSON data loader (legacy)
Key Functions:
create_tables(): Create tablesdownload_file(): Download fileextract_zip(): Extract zipget_all_printings_psql_file(): Get PSQL fileparse_psql_file(): Parse PSQLimport_cards(): Import cardsimport_all_printings_psql(): Import AllPrintingsimport_all_set_files(): Import set filesimport_all_identifiers(): Import identifiersimport_all_deck_files(): Import deck filesimport_simple_json(): Import JSONcreate_indexes(): Create indexesshow_summary(): Show summarymain(): Main function
Imports From:
app.core.settings:get_settingssqlalchemy.ext.asyncio:create_async_engine,AsyncSessionsqlalchemy.orm:sessionmakersqlalchemy:text,insert,update,select,Table,Column, etc.sqlalchemy.dialects.postgresql:insert as pg_insert
Imported By:
services/__init__.py
Dependencies: core/settings.py, sqlalchemy
5.14 mtgjson_manager.py (1296 lines)
Purpose: MTGJSON data manager (local file processing)
Key Classes/Functions:
MTGJSONManager: Main manager classget_last_refresh(): Get last refreshget_health_status(): Get health statuscheck_required_files(): Check filesupsert_all(): Upsert all data_upsert_sets(): Upsert sets_upsert_cards(): Upsert cards_upsert_cards_from_psql(): Upsert from PSQL_upsert_identifiers(): Upsert identifiers_upsert_card_types(): Upsert card types_upsert_keywords(): Upsert keywords_upsert_set_list(): Upsert set list_upsert_deck_list(): Upsert deck list_upsert_deck_files_from_zip(): Upsert deck fileslog_refresh(): Log refreshsync_mirrors(): Sync mirrorsrun_refresh(): Run refreshdownload_files(): Download filesverify_files(): Verify filesdownload_and_refresh(): Download and refreshget_manager(): Get manager instance
Imports From:
app.core.database:mtg_async_sessionjson,logging,zipfile,tempfile,shutil: Stdlibpathlib:Pathdatetime:datetimetyping:Optional,Dict,List,Tuplere: Regexaiohttp:ClientSession,ClientTimeoutasyncio:sleepgzip: Decompressionsqlalchemy:text
Imported By:
routers/games_router.pyrouters/refresh.pyservices/__init__.py
Dependencies: core/database.py, aiohttp, sqlalchemy
5.15 mtgjson_uploader.py (883 lines)
Purpose: MTGJSON data uploader (legacy)
Key Functions:
create_tables(): Create tablesimport_all_printings_psql(): Import PSQLimport_all_set_files(): Import set filesimport_all_identifiers(): Import identifiersimport_all_deck_files(): Import deck filesimport_json_files(): Import JSONdownload_file(): Download fileextract_zip(): Extract zipmain(): Main function
Imports From:
app.core.settings:get_settingssqlalchemy.ext.asyncio:create_async_engine,AsyncSessionsqlalchemy.orm:sessionmakersqlalchemy:text,insert,update,select,and_,Table,MetaData,Column, etc.sqlalchemy.dialects.postgresql:insert as pg_insertgzip,json,os,sys,zipfile: Stdlibpathlib:Pathdatetime:datetimetyping:Anyurllib.request:urlretrieve
Imported By:
services/__init__.py
Dependencies: core/settings.py, sqlalchemy
Dependency Graph Summary
┌─────────────────────────────────────────────────────────────────────────┐
│ DEPENDENCY GRAPH │
└─────────────────────────────────────────────────────────────────────────┘
core/
├── config.py (standalone)
│ └── imported by: database.py, security.py, settings.py
│
├── database.py
│ ├── imports: config.py
│ └── imported by: models/, routers/*, services/*
│
├── logging.py (standalone)
│ └── imported by: services/*
│
├── logging_config.py (DUPLICATE of logging.py)
│
├── redis_client.py
│ ├── imports: settings.py
│ └── imported by: routers/card_router.py, cards_router.py, interactions.py
│
├── security.py
│ ├── imports: config.py
│ └── imported by: routers/auth.py, auth_router.py, users.py, users_router.py, admin.py, rooms.py, games_router.py
│
└── settings.py
├── imports: config.py
└── imported by: redis_client.py, services/mtgjson_loader.py, services/mtgjson_uploader.py
models/
├── models.py
│ ├── imports: database.py
│ └── imported by: models/__init__.py, routers/*, services/*
│
├── mtg_models.py
│ ├── imports: database.py
│ └── imported by: models/__init__.py, routers/card_router.py, cards_router.py, services/card_search_service.py, services/deck_suggestion_service.py
│
└── __init__.py
├── imports: models.py, mtg_models.py
└── imported by: routers/*, services/*
routers/
├── auth.py (OLD - superseded by auth_router.py)
├── auth_router.py
│ ├── imports: database.py, security.py, models.py, schemas.py
│ └── imported by: app/main.py
│
├── users.py (OLD - superseded by users_router.py)
├── users_router.py
│ ├── imports: database.py, security.py, models.py, schemas.py
│ └── imported by: app/main.py
│
├── decks.py (OLD - superseded by decks_router.py)
├── decks_router.py
│ ├── imports: database.py, security.py, models.py, models/mtg_models.py, models/user_deck.py, services/deck_manager.py, services/deck_parser.py
│ └── imported by: app/main.py
│
├── card_router.py (OLD - superseded by cards_router.py)
├── cards_router.py
│ ├── imports: database.py, redis_client.py, models.py, models/user_deck.py, schemas/card_search_schemas.py, services/card_search_service.py, services/deck_suggestion_service.py, services/fuzzy_card_matcher.py
│ └── imported by: app/main.py
│
├── interactions.py (OLD - superseded by cards_router.py)
├── user_data.py (OLD - superseded by user_data_router.py)
├── user_data_router.py
│ ├── imports: database.py, security.py, models.py, models/user_data.py, models/card_import_batch.py, models/user_card_import_record.py, models/user_deck.py, services/file_parser.py, services/import_batch_processor.py, services/fuzzy_card_matcher.py, schemas/*
│ └── imported by: app/main.py
│
├── rooms.py (OLD - superseded by games_router.py)
├── games/__init__.py
├── games/router.py (OLD - superseded by games_router.py)
├── games_router.py
│ ├── imports: database.py, security.py, models.py, schemas.py, services/mtgjson_manager.py, services/game_server.py
│ └── imported by: app/main.py
│
├── refresh.py (OLD - superseded by games_router.py)
├── card_import.py (OLD - superseded by user_data_router.py)
├── ws.py (OLD - superseded by games_router.py)
│
└── __init__.py
├── imports: auth_router.py, users_router.py, decks_router.py, cards_router.py, user_data_router.py, games_router.py
└── imported by: app/main.py
schemas/
├── schemas.py (standalone)
│ └── imported by: routers/auth.py, auth_router.py, users.py, users_router.py, rooms.py, games_router.py
│
├── user_data_schemas.py (standalone)
│ └── imported by: routers/user_data.py, user_data_router.py
│
├── user_deck_schemas.py (standalone)
│ └── imported by: routers/decks.py, decks_router.py, card_import.py
│
├── user_card_collection.py (standalone)
│ └── imported by: routers/user_data.py, user_data_router.py
│
├── card_import_schemas.py (standalone)
│ └── imported by: routers/card_import.py, user_data_router.py
│
├── card_search_schemas.py (standalone)
│ └── imported by: routers/card_router.py, cards_router.py
│
├── mtg_card_schemas.py (standalone)
│ └── imported by: routers/cards_router.py
│
├── game_schemas.py (standalone)
│ └── imported by: routers/games_router.py
│
├── generic_schemas.py (standalone)
│ └── imported by: routers/user_data.py, user_data_router.py
│
├── proto_messages.py (UNUSED)
├── protocol_constants.py (UNUSED)
│
└── __init__.py
├── imports: all schema files
└── imported by: routers/*, services/*
services/
├── card_database.py
│ ├── imports: models/mtg_models.py, core/database.py
│ └── imported by: routers/card_router.py
│
├── card_mirror_service.py (UNUSED)
│ ├── imports: models/mtg_models.py, models/mirror_models.py, core/database.py
│
├── card_search_service.py
│ ├── imports: models/mtg_models.py, models/mirror_models.py
│ └── imported by: routers/card_router.py, cards_router.py
│
├── deck_manager.py
│ ├── imports: models/user_deck.py, models/models.py
│ └── imported by: routers/decks.py, decks_router.py
│
├── deck_parser.py (standalone)
│ └── imported by: routers/decks.py, decks_router.py
│
├── deck_suggestion_service.py
│ ├── imports: models/user_deck.py, models/mtg_models.py, models/mirror_models.py, services/fuzzy_card_matcher.py
│ └── imported by: routers/card_router.py, cards_router.py
│
├── file_parser.py
│ ├── imports: csv, json, pathlib, openpyxl, pandas
│ └── imported by: routers/card_import.py, user_data_router.py
│
├── fuzzy_card_matcher.py
│ ├── imports: thefuzz
│ └── imported by: routers/card_router.py, cards_router.py, card_import.py, user_data_router.py, services/deck_suggestion_service.py, services/import_batch_processor.py
│
├── import_batch_processor.py
│ ├── imports: models/card_import_batch.py, models/user_card_import_record.py, models/user_data.py, models/models.py, services/fuzzy_card_matcher.py
│ └── imported by: routers/card_import.py, user_data_router.py
│
├── game_server.py
│ ├── imports: asyncio, json, typing, datetime, fastapi
│ └── imported by: routers/games_router.py, routers/ws.py
│
├── mtgjson_downloader.py (standalone)
│ └── imported by: services/__init__.py
│
├── mtgjson_loader.py (LEGACY)
│ ├── imports: core/settings.py, sqlalchemy
│ └── imported by: services/__init__.py
│
├── mtgjson_manager.py
│ ├── imports: core/database.py, aiohttp, sqlalchemy
│ └── imported by: routers/games_router.py, routers/refresh.py, services/__init__.py
│
├── mtgjson_uploader.py (LEGACY)
│ ├── imports: core/settings.py, sqlalchemy
│ └── imported by: services/__init__.py
│
└── __init__.py
├── imports: all service modules
└── imported by: routers/*
Critical Issues Summary
1. Router Duplication (HIGH PRIORITY)
Every major feature has both old and new implementations:
auth.pyvsauth_router.pyusers.pyvsusers_router.pydecks.pyvsdecks_router.pycard_router.pyvscards_router.pyinteractions.pyvscards_router.py(interactions)user_data.pyvsuser_data_router.pyrooms.pyvsgames_router.py(rooms)games/router.pyvsgames_router.py(games)refresh.pyvsgames_router.py(refresh)card_import.pyvsuser_data_router.py(import)ws.pyvsgames_router.py(websocket)
Recommendation: Remove all old versions and keep only the _router.py versions.
2. Logging Duplication (MEDIUM PRIORITY)
logging.pyandlogging_config.pyare exact duplicates
Recommendation: Remove logging_config.py.
3. Settings Shim (LOW PRIORITY)
settings.pyis just a backward compatibility shim forconfig.py
Recommendation: Remove once all callers migrate to config.py.
4. Unused Code (LOW PRIORITY)
services/card_mirror_service.py: Defined but not usedschemas/proto_messages.py: Protocol buffer messages defined but not usedschemas/protocol_constants.py: Protocol constants defined but not used
Recommendation: Remove or implement if needed.
5. Model Inconsistency (MEDIUM PRIORITY)
- Multiple model files with overlapping functionality
models/user_deck.py,models/user_data.py,models/card_import_batch.py, etc. exist butmodels.pyconsolidates many of these
Recommendation: Consolidate all models into models.py and mtg_models.py.
6. Service Redundancy (MEDIUM PRIORITY)
mtgjson_loader.pyandmtgjson_uploader.pyare legacy scriptsmtgjson_manager.pyis the current implementation
Recommendation: Remove legacy scripts.
Recommended Architecture Simplification
Phase 1: Remove Duplicates
- Remove old router files (auth.py, users.py, decks.py, card_router.py, interactions.py, user_data.py, rooms.py, games/router.py, refresh.py, card_import.py, ws.py)
- Remove
logging_config.py - Remove
settings.py(migrate callers toconfig.py)
Phase 2: Consolidate Models
- Move all models from
user_deck.py,user_data.py,card_import_batch.py, etc. intomodels.py - Remove individual model files
Phase 3: Clean Up Services
- Remove
card_mirror_service.py(unused) - Remove
mtgjson_loader.pyandmtgjson_uploader.py(legacy) - Remove
proto_messages.pyandprotocol_constants.py(unused)
Phase 4: Standardize Patterns
- Ensure all routers use consistent patterns
- Ensure all services use consistent patterns (ORM vs raw SQL)
- Add type hints and docstrings where missing
Statistics
- Total Files Analyzed: 50+
- Total Lines of Code: ~30,000+
- Old Router Files (to remove): 11
- New Router Files (to keep): 6
- Duplicate Files: 2 (logging, settings)
- Unused Files: 4 (card_mirror_service, proto_messages, protocol_constants, mtgjson_loader/uploader)
Conclusion
The MTG Online backend has significant architectural debt with multiple layers of duplication. The codebase is in a transition state where new consolidated routers and services have been created but old versions still exist.
Immediate Actions:
- Remove all old router files (11 files)
- Remove duplicate logging config
- Remove unused schema files
- Consolidate models
Long-term:
- Standardize on ORM or raw SQL (currently mixed)
- Add comprehensive tests
- Improve error handling
- Add proper logging throughout
The project is functional but needs cleanup to be maintainable.