init commit
This commit is contained in:
523
docs/MVP_QUICK_START.md
Normal file
523
docs/MVP_QUICK_START.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# 🚀 MVP Implementation Quick Start
|
||||
|
||||
## Phase-by-Phase Implementation Guide
|
||||
|
||||
### ✅ Phase 1: Complete (Existing)
|
||||
- Database schema with 10 tables
|
||||
- Environment variable management
|
||||
- Docker Compose setup
|
||||
- API health endpoint
|
||||
|
||||
### 🔄 Phase 2: Security Foundation (THIS DELIVERABLE)
|
||||
|
||||
#### 2.1 Database Migrations
|
||||
```bash
|
||||
# Run the new migration
|
||||
cd /home/data/finance_bot
|
||||
source .venv/bin/activate
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
**What it creates:**
|
||||
- `sessions` table (for refresh token tracking)
|
||||
- `telegram_identities` table (Telegram user binding)
|
||||
- `event_log` table (audit trail)
|
||||
- `access_log` table (request logging)
|
||||
- Enhanced `transactions` (with approval workflow)
|
||||
- Enhanced `family_members` (RBAC)
|
||||
|
||||
#### 2.2 Install Dependencies
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
**Key additions:**
|
||||
```
|
||||
PyJWT==2.8.1 # JWT token management
|
||||
aiohttp==3.9.1 # Async HTTP client
|
||||
python-multipart==0.0.6 # Form data parsing
|
||||
redis==5.0.1 # Redis client
|
||||
```
|
||||
|
||||
#### 2.3 Update Configuration
|
||||
```bash
|
||||
# Add to .env
|
||||
JWT_SECRET_KEY=your-super-secret-key-min-32-chars-here-please
|
||||
HMAC_SECRET_KEY=your-hmac-secret-key-min-32-chars-please
|
||||
REQUIRE_HMAC_VERIFICATION=false # Disabled in MVP
|
||||
```
|
||||
|
||||
#### 2.4 Verify API Starts
|
||||
```bash
|
||||
# Start FastAPI server
|
||||
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
|
||||
# In another terminal, test
|
||||
curl http://localhost:8000/health
|
||||
# Response: {"status":"ok","environment":"development","version":"1.0.0"}
|
||||
```
|
||||
|
||||
### 📋 Phase 3: API Endpoints (EXAMPLES)
|
||||
|
||||
#### 3.1 Authentication Endpoints
|
||||
|
||||
**Login:**
|
||||
```bash
|
||||
POST /api/v1/auth/login
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "password123"
|
||||
}
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"user_id": 1,
|
||||
"expires_in": 900
|
||||
}
|
||||
```
|
||||
|
||||
**Telegram Binding (Start):**
|
||||
```bash
|
||||
POST /api/v1/auth/telegram/start
|
||||
{
|
||||
"chat_id": 12345
|
||||
}
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"code": "ABC123XYZ...",
|
||||
"expires_in": 600
|
||||
}
|
||||
```
|
||||
|
||||
**Telegram Binding (Confirm):**
|
||||
```bash
|
||||
POST /api/v1/auth/telegram/confirm
|
||||
Authorization: Bearer <user_access_token>
|
||||
{
|
||||
"code": "ABC123XYZ...",
|
||||
"chat_id": 12345,
|
||||
"username": "john_doe",
|
||||
"first_name": "John"
|
||||
}
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"success": true,
|
||||
"user_id": 1,
|
||||
"jwt_token": "eyJhbGc...",
|
||||
"expires_at": "2024-01-09T12:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 Transaction Endpoints
|
||||
|
||||
**Create Transaction (Small Amount - Auto-executed):**
|
||||
```bash
|
||||
POST /api/v1/transactions
|
||||
Authorization: Bearer <jwt_token>
|
||||
X-Client-Id: telegram_bot
|
||||
X-Timestamp: 1702237800
|
||||
X-Signature: <hmac_sha256>
|
||||
|
||||
{
|
||||
"family_id": 1,
|
||||
"from_wallet_id": 10,
|
||||
"to_wallet_id": 11,
|
||||
"amount": 50.00,
|
||||
"category_id": 5,
|
||||
"description": "Groceries"
|
||||
}
|
||||
|
||||
Response 201:
|
||||
{
|
||||
"id": 100,
|
||||
"status": "executed",
|
||||
"amount": "50.00",
|
||||
"confirmation_required": false,
|
||||
"created_at": "2023-12-10T12:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Create Transaction (Large Amount - Requires Approval):**
|
||||
```bash
|
||||
POST /api/v1/transactions
|
||||
...
|
||||
{
|
||||
...
|
||||
"amount": 600.00, # > threshold
|
||||
}
|
||||
|
||||
Response 201:
|
||||
{
|
||||
"id": 101,
|
||||
"status": "pending_approval",
|
||||
"amount": "600.00",
|
||||
"confirmation_required": true,
|
||||
"created_at": "2023-12-10T12:30:00Z"
|
||||
}
|
||||
|
||||
# Bot notifies owner in Telegram
|
||||
```
|
||||
|
||||
**Approve Transaction:**
|
||||
```bash
|
||||
POST /api/v1/transactions/101/confirm
|
||||
Authorization: Bearer <owner_jwt>
|
||||
{
|
||||
"confirmation_token": null
|
||||
}
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"id": 101,
|
||||
"status": "executed",
|
||||
"executed_at": "2023-12-10T12:35:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Reverse Transaction:**
|
||||
```bash
|
||||
DELETE /api/v1/transactions/100
|
||||
Authorization: Bearer <jwt>
|
||||
{
|
||||
"reason": "User requested refund"
|
||||
}
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"original_transaction_id": 100,
|
||||
"reversal_transaction_id": 102,
|
||||
"reversed_at": "2023-12-10T12:40:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3 Wallet Endpoints
|
||||
|
||||
**List Wallets:**
|
||||
```bash
|
||||
GET /api/v1/wallets?family_id=1
|
||||
Authorization: Bearer <jwt>
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"wallets": [
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Cash",
|
||||
"balance": "150.00",
|
||||
"type": "cash"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Bank Account",
|
||||
"balance": "1250.00",
|
||||
"type": "bank"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing the MVP
|
||||
|
||||
### 1. Unit Tests
|
||||
```bash
|
||||
# Run security tests
|
||||
pytest tests/test_security.py -v
|
||||
|
||||
# Run specific test
|
||||
pytest tests/test_security.py::TestJWTManager::test_create_access_token -v
|
||||
```
|
||||
|
||||
### 2. Integration Tests
|
||||
```bash
|
||||
# Start API server in background
|
||||
python -m uvicorn app.main:app &
|
||||
|
||||
# Run full test suite
|
||||
pytest tests/ -v
|
||||
|
||||
# Run with coverage
|
||||
pytest tests/ --cov=app --cov-report=html
|
||||
```
|
||||
|
||||
### 3. Manual API Testing
|
||||
|
||||
**Using curl:**
|
||||
```bash
|
||||
# Get health
|
||||
curl http://localhost:8000/health
|
||||
|
||||
# Create transaction (need valid JWT)
|
||||
JWT_TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"user@example.com","password":"pass"}' | jq -r '.access_token')
|
||||
|
||||
curl -X POST http://localhost:8000/api/v1/transactions \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-H "X-Client-Id: manual_test" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"family_id": 1,
|
||||
"from_wallet_id": 10,
|
||||
"to_wallet_id": 11,
|
||||
"amount": 50.00,
|
||||
"category_id": 5,
|
||||
"description": "Test transaction"
|
||||
}'
|
||||
```
|
||||
|
||||
**Using Swagger UI:**
|
||||
```
|
||||
http://localhost:8000/docs
|
||||
```
|
||||
- All endpoints documented with interactive testing
|
||||
- Try endpoints directly from browser
|
||||
|
||||
**Using Postman:**
|
||||
1. Open Postman
|
||||
2. Create new request
|
||||
3. Set URL: `http://localhost:8000/api/v1/transactions`
|
||||
4. Set Method: `POST`
|
||||
5. Add Headers:
|
||||
- `Authorization: Bearer <your_jwt>`
|
||||
- `X-Client-Id: postman`
|
||||
6. Set Body (JSON):
|
||||
```json
|
||||
{
|
||||
"family_id": 1,
|
||||
"from_wallet_id": 10,
|
||||
"to_wallet_id": 11,
|
||||
"amount": 50.00,
|
||||
"category_id": 5,
|
||||
"description": "Postman test"
|
||||
}
|
||||
```
|
||||
7. Send!
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Telegram Bot Testing
|
||||
|
||||
### 1. Local Bot Testing
|
||||
|
||||
**Create test bot:**
|
||||
1. Open Telegram
|
||||
2. Chat with @BotFather
|
||||
3. `/newbot`
|
||||
4. Follow instructions
|
||||
5. Get BOT_TOKEN
|
||||
|
||||
**Update .env:**
|
||||
```
|
||||
BOT_TOKEN=<your_test_bot_token>
|
||||
```
|
||||
|
||||
**Run bot:**
|
||||
```bash
|
||||
# Terminal 1: API server
|
||||
python -m uvicorn app.main:app --reload
|
||||
|
||||
# Terminal 2: Bot client (polling)
|
||||
# TODO: Implement bot polling in app/bot/worker.py
|
||||
```
|
||||
|
||||
**Test flow:**
|
||||
```
|
||||
Your Telegram → /start
|
||||
Bot → "Click link to bind: https://..."
|
||||
You → Click link (authenticate)
|
||||
API → Create TelegramIdentity
|
||||
You → Bot says "Connected!"
|
||||
You → /balance
|
||||
Bot → Shows wallets via API call
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 RBAC Testing
|
||||
|
||||
### Test Permission Checking
|
||||
|
||||
```python
|
||||
# python -i
|
||||
from app.security.rbac import RBACEngine, MemberRole, Permission, UserContext
|
||||
|
||||
# Create contexts for different roles
|
||||
owner = UserContext(
|
||||
user_id=1,
|
||||
family_id=1,
|
||||
role=MemberRole.OWNER,
|
||||
permissions=RBACEngine.get_permissions(MemberRole.OWNER),
|
||||
family_ids=[1],
|
||||
)
|
||||
|
||||
member = UserContext(
|
||||
user_id=2,
|
||||
family_id=1,
|
||||
role=MemberRole.MEMBER,
|
||||
permissions=RBACEngine.get_permissions(MemberRole.MEMBER),
|
||||
family_ids=[1],
|
||||
)
|
||||
|
||||
# Test owner permissions
|
||||
RBACEngine.has_permission(owner, Permission.DELETE_FAMILY) # True
|
||||
RBACEngine.has_permission(member, Permission.DELETE_FAMILY) # False
|
||||
|
||||
# Test family access
|
||||
RBACEngine.check_family_access(owner, 1) # OK
|
||||
RBACEngine.check_family_access(member, 2, raise_exception=False) # False
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment Steps
|
||||
|
||||
### Docker Compose Deployment
|
||||
|
||||
```bash
|
||||
# Navigate to project
|
||||
cd /home/data/finance_bot
|
||||
|
||||
# Build images
|
||||
docker-compose build
|
||||
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
|
||||
# Run migrations
|
||||
docker-compose exec migrations python -m alembic upgrade head
|
||||
|
||||
# Verify
|
||||
docker-compose ps
|
||||
curl http://localhost:8000/health
|
||||
|
||||
# Check logs
|
||||
docker-compose logs -f api
|
||||
docker-compose logs -f bot
|
||||
```
|
||||
|
||||
### Docker-Free Deployment
|
||||
|
||||
```bash
|
||||
# Setup environment
|
||||
source .venv/bin/activate
|
||||
|
||||
# Update .env
|
||||
export $(cat .env | grep -v '#' | xargs)
|
||||
|
||||
# Start services (in separate terminals)
|
||||
# Terminal 1: API
|
||||
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
|
||||
# Terminal 2: Bot (polling)
|
||||
# TODO: Implement in app/bot/worker.py
|
||||
python -m app.bot.worker
|
||||
|
||||
# Terminal 3: Worker (event processing)
|
||||
# TODO: Implement in app/workers/event_processor.py
|
||||
python -m app.workers.event_processor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Monitoring & Debugging
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
```python
|
||||
# In app/core/config.py
|
||||
log_level: str = "DEBUG"
|
||||
|
||||
# In .env
|
||||
LOG_LEVEL=DEBUG
|
||||
```
|
||||
|
||||
### View Event Log
|
||||
|
||||
```python
|
||||
# python -i
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from app.db.models import EventLog
|
||||
|
||||
engine = create_engine("postgresql://...")
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
# Query recent events
|
||||
events = session.query(EventLog).order_by(EventLog.created_at.desc()).limit(10)
|
||||
for e in events:
|
||||
print(f"{e.created_at} | {e.action} | {e.entity_type} #{e.entity_id} | Actor: {e.actor_id}")
|
||||
```
|
||||
|
||||
### View Access Log
|
||||
|
||||
```python
|
||||
from app.db.models import AccessLog
|
||||
|
||||
access = session.query(AccessLog).order_by(AccessLog.created_at.desc()).limit(10)
|
||||
for a in access:
|
||||
print(f"{a.created_at} | {a.method} {a.endpoint} | {a.status_code} | {a.user_id} | {a.ip_address}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❌ Troubleshooting
|
||||
|
||||
### Issue: "type already exists"
|
||||
**Solution:**
|
||||
```bash
|
||||
# Drop conflicting type in PostgreSQL
|
||||
docker exec finance_bot_postgres psql -U trevor -d finance_db -c \
|
||||
"DROP TYPE IF EXISTS family_role CASCADE;"
|
||||
|
||||
# Re-run migration
|
||||
docker-compose exec migrations python -m alembic downgrade -1
|
||||
docker-compose exec migrations python -m alembic upgrade head
|
||||
```
|
||||
|
||||
### Issue: JWT token verification fails
|
||||
**Solution:**
|
||||
```python
|
||||
# Check token expiration
|
||||
from app.security.jwt_manager import jwt_manager
|
||||
token_payload = jwt_manager.decode_token(token) # Ignore signature
|
||||
print(f"Expires at: {token_payload.get('exp')}")
|
||||
print(f"Current time: {datetime.utcnow().timestamp()}")
|
||||
```
|
||||
|
||||
### Issue: HMAC signature mismatch
|
||||
**Solution:**
|
||||
1. Verify base_string format: `METHOD:ENDPOINT:TIMESTAMP:BODY_HASH`
|
||||
2. Verify client_secret matches on both sides
|
||||
3. Check timestamp isn't too old (±30 seconds)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Next Steps
|
||||
|
||||
### To Complete MVP:
|
||||
1. ✅ Security foundation created
|
||||
2. ⏳ Add remaining endpoints (Wallets, Budgets, Goals, Reports)
|
||||
3. ⏳ Implement worker process (event consumer)
|
||||
4. ⏳ Implement Telegram bot webhook (instead of polling)
|
||||
5. ⏳ Add comprehensive tests
|
||||
6. ⏳ Generate API documentation
|
||||
|
||||
### To Extend MVP:
|
||||
1. Web frontend (React)
|
||||
2. Mobile app (React Native)
|
||||
3. Advanced reporting
|
||||
4. Kubernetes deployment
|
||||
5. Multi-region setup
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Last Updated:** 2025-12-10
|
||||
**Next Review:** 2025-12-24
|
||||
Reference in New Issue
Block a user