524 lines
10 KiB
Markdown
524 lines
10 KiB
Markdown
# 🚀 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
|