lotr-sut/sut/backend/routes/shop.py
Fellowship Scholar f6a5823439 init commit
2026-03-29 20:07:56 +00:00

134 lines
4.2 KiB
Python

"""Shop routes for bargaining market gameplay."""
from typing import Any, Dict, Optional
from flask import Blueprint, request, session
from flask_restx import Api, Resource, fields
from models.user import User
from services.shop_service import ShopService
shop_bp = Blueprint('shop', __name__, url_prefix='/api')
shop_api = Api(shop_bp, doc=False, prefix='/shop')
purchase_model = shop_api.model('ShopPurchaseRequest', {
'item_id': fields.Integer(required=True, description='Unique item ID'),
'paid_price': fields.Integer(required=True, description='Agreed paid price'),
})
def _require_auth() -> bool:
return session.get('user_id') is not None
def _get_current_user() -> Optional[User]:
user_id = session.get('user_id')
if not user_id:
return None
return User.query.get(user_id)
@shop_api.route('/items')
class ShopItems(Resource):
def get(self) -> tuple[Dict[str, Any], int]:
if not _require_auth():
return {'error': 'Authentication required'}, 401
character = (request.args.get('character') or '').strip().lower() or None
items = ShopService.list_available_items(character=character)
return {'items': items}, 200
@shop_api.route('/items/<int:item_id>')
class ShopItemDetail(Resource):
def get(self, item_id: int) -> tuple[Dict[str, Any], int]:
if not _require_auth():
return {'error': 'Authentication required'}, 401
item = ShopService.get_item_public(item_id)
if not item:
return {'error': 'Item not found'}, 404
return {'item': item}, 200
@shop_api.route('/purchase')
class ShopPurchase(Resource):
@shop_api.expect(purchase_model)
def post(self) -> tuple[Dict[str, Any], int]:
if not _require_auth():
return {'error': 'Authentication required'}, 401
user = _get_current_user()
if not user:
return {'error': 'User not found'}, 404
payload = request.get_json() or {}
item_id = payload.get('item_id')
paid_price = payload.get('paid_price')
if not item_id or paid_price is None:
return {'error': 'item_id and paid_price are required'}, 400
try:
result = ShopService.purchase_item(user_id=user.id, item_id=int(item_id), paid_price=int(paid_price))
return result, 200
except ValueError as error:
return {'error': str(error)}, 400
@shop_api.route('/inventory')
class ShopInventory(Resource):
def get(self) -> tuple[Dict[str, Any], int]:
if not _require_auth():
return {'error': 'Authentication required'}, 401
user = _get_current_user()
if not user:
return {'error': 'User not found'}, 404
inventory = ShopService.get_user_inventory(user.id)
return {'inventory': inventory}, 200
@shop_api.route('/stats')
class ShopStats(Resource):
def get(self) -> tuple[Dict[str, Any], int]:
if not _require_auth():
return {'error': 'Authentication required'}, 401
user = _get_current_user()
if not user:
return {'error': 'User not found'}, 404
stats = ShopService.get_user_stats(user.id)
return {'stats': stats}, 200
@shop_api.route('/balance')
class ShopBalance(Resource):
def get(self) -> tuple[Dict[str, Any], int]:
if not _require_auth():
return {'error': 'Authentication required'}, 401
user = _get_current_user()
if not user:
return {'error': 'User not found'}, 404
return ShopService.get_balance(user.id), 200
@shop_api.route('/test-reset')
class TestReset(Resource):
"""Reset shop state for testing - marks all items as not sold and resets user gold."""
def post(self) -> tuple[Dict[str, Any], int]:
import os
# Only allow in non-production environments
if os.getenv('FLASK_ENV') in {'production', 'prod'}:
return {'error': 'Test reset not allowed in production'}, 403
try:
ShopService.reset_for_tests()
return {'success': True, 'message': 'Test state reset successfully'}, 200
except Exception as e:
return {'error': str(e)}, 500