8.3 KiB
PostgreSQL Database Implementation Summary
What Was Done
Successfully migrated from in-memory fake_db dictionaries to PostgreSQL with async SQLAlchemy ORM.
Files Modified
1. database/database.py - COMPLETELY REWRITTEN
Changes:
- ✅ Converted from synchronous to async SQLAlchemy (using
asyncpgdriver) - ✅ Added proper
Usermodel with:- Auto-increment
idas primary key - Unique indexed
telegram_idandtoken created_atandupdated_attimestamps- Composite index on
tokenandstatus
- Auto-increment
- ✅ Added
Profilemodel with:- All fields from your Pydantic model
- Email as unique indexed field
- Timestamps
- ✅ Created
get_db()dependency for FastAPI - ✅ Added
init_db()anddrop_db()utility functions - ✅ Configured connection pooling and async engine
2. app.py - MAJOR UPDATES
Changes:
- ✅ Removed
profile_db = {}in-memory dict - ✅ Added database imports and
Depends(get_db)to all endpoints - ✅ Added
@app.on_event("startup")to initialize DB on app start - ✅ Updated
/profilePOST endpoint:- Now saves to PostgreSQL
profilestable - Handles create/update logic
- Properly commits transactions
- Now saves to PostgreSQL
- ✅ Updated
/profile/{email}GET endpoint:- Queries from PostgreSQL
- Converts DB model to Pydantic response
- ✅ Updated
/loginendpoint:- Creates
Userrecord with pending status - Stores in PostgreSQL instead of dict
- Creates
- ✅ Updated
/check-auth/{token}endpoint:- Queries user by token from PostgreSQL
- Returns proper status
- ✅ Updated
/database/tokensendpoint:- Lists all users from database
3. bot.py - MAJOR REFACTORING
Changes:
- ✅ Removed all references to
fake_db - ✅ Removed
app.db = fake_dbsynchronization code - ✅ Added proper database imports
- ✅ Updated
/startcommand handler:- Uses
AsyncSessionLocal()for DB sessions - Queries user by token
- Updates telegram_id, username, and status
- Proper error handling with rollback
- Uses
- ✅ Added
init_db()call instart_bot()
4. requirements.txt - CREATED
New dependencies:
- FastAPI + Uvicorn
- Pydantic with email support
- SQLAlchemy 2.0 with async support
- asyncpg (PostgreSQL async driver)
- psycopg2-binary (backup driver)
- greenlet (required for SQLAlchemy async)
- aiogram 3.3.0 (Telegram bot)
- minio (file storage)
- python-dotenv
- Testing: pytest, pytest-asyncio, httpx
5. init_db.py - CREATED
Purpose: Interactive script to initialize or reset database Features:
- Option 1: Create tables
- Option 2: Drop and recreate (reset)
- Safe with confirmation prompts
6. README.md - COMPLETELY REWRITTEN
New content:
- Complete setup instructions
- Database schema documentation
- API endpoints reference
- Usage flow diagram
- Development guide
- Troubleshooting section
Database Schema
Users Table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
telegram_id INTEGER NOT NULL UNIQUE,
token VARCHAR(255) NOT NULL UNIQUE,
username VARCHAR(100),
status VARCHAR(50) NOT NULL DEFAULT 'pending',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_token_status ON users(token, status);
CREATE UNIQUE INDEX ix_users_telegram_id ON users(telegram_id);
CREATE UNIQUE INDEX ix_users_token ON users(token);
Profiles Table
CREATE TABLE profiles (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL,
position VARCHAR(255) NOT NULL,
competencies TEXT,
experience TEXT,
skills TEXT,
country VARCHAR(100),
languages VARCHAR(255),
employment_format VARCHAR(100),
rate VARCHAR(100),
relocation VARCHAR(100),
cv_url VARCHAR(500),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX ix_profiles_email ON profiles(email);
Architecture Benefits
Before (In-Memory Dicts)
❌ Data lost on restart ❌ No persistence ❌ No concurrent access control ❌ No data validation at DB level ❌ No relationships or constraints ❌ No transaction safety
After (PostgreSQL + SQLAlchemy)
✅ Persistent - Data survives restarts ✅ ACID compliant - Transaction safety ✅ Concurrent - Handle multiple requests ✅ Indexed - Fast queries on telegram_id, token, email ✅ Constraints - Unique tokens, emails ✅ Timestamps - Track created_at, updated_at ✅ Async - Non-blocking database operations ✅ Pooling - Efficient connection management
How It Works Now
Authentication Flow
- User visits website →
GET /login - FastAPI creates new
Userrecord in PostgreSQL:User(telegram_id=0, token=uuid4(), status='pending') - Returns Telegram bot URL with token
- User clicks link → Opens bot → Sends
/start {token} - Bot queries database for token:
user = await session.execute(select(User).where(User.token == token)) - Bot updates user:
user.telegram_id = message.from_user.id user.username = message.from_user.username user.status = 'success' await session.commit() - Website polls
/check-auth/{token}→ Gets auth status from DB
Profile Management Flow
- User submits profile →
POST /profile - FastAPI uploads CV to MinIO
- Checks if profile exists:
existing = await db.execute(select(Profile).where(Profile.email == email)) - Updates existing or creates new profile
- Commits to PostgreSQL
Testing the Implementation
1. Initialize Database
python init_db.py
# Choose option 1 to create tables
2. Verify Tables
docker exec rag_ai_postgres psql -U postgres -d rag_ai_assistant -c "\dt"
# Should show: users, profiles
3. Test Database Connection
.venv/bin/python database/database.py
# Should create test user and retrieve it
4. Start Application
# Option A: Together
python bot.py
# Option B: Separate terminals
uvicorn app:app --reload
# In another terminal:
python -c "from bot import start_bot; import asyncio; asyncio.run(start_bot())"
5. Test Endpoints
# Test login
curl http://localhost:8000/login
# Test check-auth
curl http://localhost:8000/check-auth/{token}
# Test tokens list
curl http://localhost:8000/database/tokens
Common Issues & Solutions
Issue: "No module named 'sqlalchemy'"
Solution: Install dependencies
uv pip install -r requirements.txt
Issue: "the greenlet library is required"
Solution: Already added to requirements.txt
uv pip install greenlet==3.0.3
Issue: "Connection refused" to PostgreSQL
Solution: Start Docker services
docker-compose up -d
docker ps # Verify postgres is running
Issue: Old table structure
Solution: Reset database
python init_db.py # Choose option 2 (reset)
Next Steps (Optional Improvements)
- Add foreign key relationship between users and profiles
- Implement token expiration (add expires_at column)
- Add database migrations (Alembic)
- Add indexes for common queries
- Implement connection pooling tuning for production
- Add Redis caching for frequently accessed data
- Implement soft deletes (deleted_at column)
- Add audit logs table for tracking changes
- Create database backup scripts
- Add monitoring with Prometheus/Grafana
Code Quality Improvements Made
- ✅ Type hints throughout database code
- ✅ Docstrings on all major functions
- ✅ Error handling with try/except and rollback
- ✅ Session management using context managers
- ✅ Connection pooling with proper configuration
- ✅ Index optimization on frequently queried fields
- ✅ Async/await pattern throughout
- ✅ Environment variables for all config
- ✅ Dependency injection with FastAPI Depends()
Summary
Your application now has a production-ready database layer with:
- ✅ Proper ORM models
- ✅ Async database operations
- ✅ Transaction safety
- ✅ Data persistence
- ✅ Proper indexing
- ✅ Error handling
- ✅ Clean architecture
All the logic for authentication tokens and profile storage has been successfully migrated from in-memory dictionaries to PostgreSQL!