What a mess
This commit is contained in:
296
docs/archive/API_REFACTOR_GUIDE.md
Normal file
296
docs/archive/API_REFACTOR_GUIDE.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# 🔄 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! 🚀
|
||||
Reference in New Issue
Block a user