Files
echoes-of-the-ash/docs/archive/API_REFACTOR_GUIDE.md
2025-11-07 15:27:13 +01:00

297 lines
8.6 KiB
Markdown

# 🔄 API-First Architecture Refactor
## Overview
This refactor moves game logic from the Telegram bot to the FastAPI server, making the API the **single source of truth** for all game operations.
## Benefits
**Single Source of Truth** - All game logic in one place
**Consistency** - Web and Telegram bot behave identically
**Easier Maintenance** - Fix bugs once, applies everywhere
**Better Testing** - Test game logic via API endpoints
**Scalability** - Can add more frontends (Discord, mobile app, etc.)
**Performance** - Direct database access from API
## Architecture
```
┌─────────────────┐ ┌──────────────────┐
│ Telegram Bot │◄────────►│ FastAPI API │
│ (Frontend) │ HTTP │ (Game Engine) │
└─────────────────┘ └──────────────────┘
┌────────▼────────┐
│ PostgreSQL │
│ Database │
└─────────────────┘
┌─────────────────┐
│ React PWA │◄────────►│ FastAPI API │
│ (Frontend) │ HTTP │ (Game Engine) │
└─────────────────┘ └──────────────────┘
```
## Implementation Status
### ✅ Completed
1. **API Client** (`bot/api_client.py`)
- Async HTTP client using httpx
- Methods for all game operations
- Error handling and retry logic
2. **Internal API** (`api/internal.py`)
- Protected endpoints with internal API key
- Player management (get, create, update)
- Movement logic
- Location queries
- Inventory operations
- Combat system
3. **Environment Configuration**
- `API_INTERNAL_KEY` - Secret key for bot-to-API auth
- `API_BASE_URL` - API endpoint for bot to call
4. **Dependencies**
- Added `httpx==0.25.2` to requirements.txt
### 🔄 To Be Migrated
The following bot files need to be updated to use the API client instead of direct database access:
1. **`bot/handlers.py`** - Telegram command handlers
- Use `api_client.get_player()` instead of `database.get_player()`
- Use `api_client.move_player()` instead of direct location updates
- Use `api_client.start_combat()` for combat initiation
2. **`bot/logic.py`** - Game logic functions
- Movement should call API
- Item usage should call API
- Status effects should be managed by API
3. **`bot/combat.py`** - Combat system
- Can keep combat logic here OR move to API
- Recommendation: Move to API for consistency
## Internal API Endpoints
All internal endpoints require the `X-Internal-Key` header for authentication.
### Player Management
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/internal/player/telegram/{telegram_id}` | Get player by Telegram ID |
| POST | `/api/internal/player` | Create new player |
| PATCH | `/api/internal/player/telegram/{telegram_id}` | Update player data |
### Location & Movement
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/internal/location/{location_id}` | Get location details |
| POST | `/api/internal/player/telegram/{telegram_id}/move` | Move player |
### Inventory
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/internal/player/telegram/{telegram_id}/inventory` | Get inventory |
| POST | `/api/internal/player/telegram/{telegram_id}/use_item` | Use item |
| POST | `/api/internal/player/telegram/{telegram_id}/equip` | Equip/unequip item |
### Combat
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/internal/combat/start` | Start combat |
| GET | `/api/internal/combat/telegram/{telegram_id}` | Get combat state |
| POST | `/api/internal/combat/telegram/{telegram_id}/action` | Combat action |
## Security
### Internal API Key
The internal API uses a shared secret key (`API_INTERNAL_KEY`) to authenticate bot requests:
- **Not exposed to users** - Only bot and API know it
- **Different from JWT tokens** - User auth uses JWT
- **Should be changed in production** - Use strong random key
### Network Security
- Bot and API communicate via Docker internal network
- No public exposure of internal endpoints
- Traefik only exposes public API and PWA
## Migration Guide
### Step 1: Deploy Updated Services
```bash
# Rebuild both bot and API with new code
docker compose up -d --build echoes_of_the_ashes_bot echoes_of_the_ashes_api
```
### Step 2: Test Internal API
```bash
# Test from bot container
docker exec echoes_of_the_ashes_bot python -c "
import asyncio
from bot.api_client import api_client
async def test():
player = await api_client.get_player(10101691)
print(f'Player: {player}')
asyncio.run(test())
"
```
### Step 3: Migrate Bot Handlers
Update `bot/handlers.py` to use API client:
**Before:**
```python
from bot.database import get_player, update_player
async def move_command(update, context):
player = await get_player(telegram_id=user_id)
# ... movement logic ...
await update_player(telegram_id=user_id, updates={...})
```
**After:**
```python
from bot.api_client import api_client
async def move_command(update, context):
result = await api_client.move_player(user_id, direction)
if result.get('success'):
# Handle success
else:
# Handle error
```
### Step 4: Remove Direct Database Access
Once all handlers are migrated, bot should only use:
- `api_client.*` for game operations
- `database.*` only for legacy compatibility (if needed)
## Testing
### Manual Testing
1. **Test Player Creation**
```bash
curl -X POST http://localhost:8000/api/internal/player \
-H "X-Internal-Key: bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210" \
-H "Content-Type: application/json" \
-d '{"telegram_id": 12345, "name": "TestPlayer"}'
```
2. **Test Movement**
```bash
curl -X POST http://localhost:8000/api/internal/player/telegram/12345/move \
-H "X-Internal-Key: bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210" \
-H "Content-Type: application/json" \
-d '{"direction": "north"}'
```
3. **Test Location Query**
```bash
curl -X GET http://localhost:8000/api/internal/location/start_point \
-H "X-Internal-Key: bot-internal-key-9f8e7d6c5b4a3210fedcba9876543210"
```
### Integration Testing
1. Send `/start` to Telegram bot - should still work
2. Try moving via bot - should use API
3. Try moving via PWA - should use same API
4. Verify both show same state
## Rollback Plan
If issues occur, rollback is simple:
```bash
# Revert to previous bot image
docker compose down echoes_of_the_ashes_bot
git checkout HEAD~1 bot/
docker compose up -d --build echoes_of_the_ashes_bot
```
Bot will continue using direct database access until refactor is complete.
## Performance Considerations
### Latency
- **Before:** Direct database query (~10-50ms)
- **After:** HTTP request + database query (~20-100ms)
- **Impact:** Negligible for human interaction
### Caching
Consider caching in API for:
- Location data (rarely changes)
- Item definitions (static)
- NPC templates (static)
### Connection Pooling
- httpx client reuses connections
- Database connection pool in API
- No need for bot to manage DB connections
## Monitoring
Add logging to track API calls:
```python
# In api_client.py
import logging
logger = logging.getLogger(__name__)
async def get_player(self, telegram_id: int):
logger.info(f"API call: get_player({telegram_id})")
# ... rest of method ...
```
## Future Enhancements
1. **Rate Limiting** - Prevent API abuse
2. **Request Metrics** - Track endpoint usage
3. **Error Recovery** - Automatic retry with backoff
4. **API Versioning** - `/api/v1/internal/...`
5. **GraphQL** - Consider for complex queries
## Status: IN PROGRESS
- [x] Create API client
- [x] Create internal endpoints
- [x] Add authentication
- [x] Update environment config
- [x] Fix location endpoint bug
- [ ] Migrate bot handlers
- [ ] Update bot logic
- [ ] Remove direct database access from bot
- [ ] Integration testing
- [ ] Documentation
---
**Next Steps:**
1. Deploy current changes (API fixes are ready)
2. Test internal API endpoints
3. Begin migrating bot handlers one by one
4. Full integration testing
5. Remove old database calls from bot
This refactor sets the foundation for a scalable, maintainable architecture! 🚀