feat: fix registration 500, add forgot-password flow, frontend validation
Some checks failed
Build and Deploy / Backend Tests (push) Has been cancelled
Build and Deploy / Frontend Build Check (push) Has been cancelled
Build and Deploy / Build Docker Images (push) Has been cancelled
Build and Deploy / Deploy to Server (push) Has been cancelled

- Fix MissingGreenlet: sync_engine now uses psycopg2 instead of asyncpg
- Fix bcrypt/passlib compat: pin bcrypt<4.1 in requirements
- Fix legacy password_hash NOT NULL: alter column to nullable in migration
- Add frontend password validation (uppercase + lowercase + digit)
- Add forgot-password and reset-password backend endpoints
- Add forgot-password and reset-password frontend pages
- Add email_service.py (SMTP via admin settings)
- Add reset_token/reset_token_expires columns to User model
- Migrate legacy JSON-only users to DB on password reset request
- Mount data/ volume in docker-compose.local.yml for persistence
- Add production deployment config (Dockerfile, nginx, deploy.sh)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Sepehr Ramezani
2026-05-01 16:23:51 +02:00
parent 26bd096a06
commit 2f7347b4db
25 changed files with 2765 additions and 64 deletions

View File

@@ -67,8 +67,10 @@ else:
# Kept for backward compatibility until all callers use async; see story 1-1.
# Prefer get_db() / AsyncSessionLocal for new code.
if DATABASE_URL and _is_postgres:
# Pour le sync engine, utiliser psycopg2 (pas asyncpg qui ne supporte pas les appels synchrones)
sync_url = DATABASE_URL.replace("+asyncpg", "")
sync_engine = create_engine(
DATABASE_URL,
sync_url,
poolclass=QueuePool,
pool_size=5,
max_overflow=10,

View File

@@ -83,6 +83,9 @@ class User(Base):
stripe_customer_id = Column(String(255), nullable=True, index=True)
stripe_subscription_id = Column(String(255), nullable=True)
reset_token = Column(String(255), nullable=True)
reset_token_expires = Column(DateTime, nullable=True)
docs_translated_this_month = Column(Integer, default=0)
pages_translated_this_month = Column(Integer, default=0)
api_calls_this_month = Column(Integer, default=0)