From e6d7888513a100e2c4b4ddbc7d8775c8a04d44b1 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Sun, 12 Apr 2026 16:03:03 +0200 Subject: [PATCH] Fix dev stack startup: seed path, missing migration, passlib/bcrypt incompatibility - python -m scripts.seed (module mode) fixes ModuleNotFoundError - Add scripts/__init__.py to make scripts/ a proper package - Generate initial Alembic migration for users table - Replace passlib with direct bcrypt>=4.0 (passlib unmaintained, broken with bcrypt 4.x) Co-Authored-By: Claude Sonnet 4.6 --- .../38efeff7c45a_create_users_table.py | 39 +++++++++++++++++++ backend/app/core/security.py | 8 ++-- backend/pyproject.toml | 2 +- backend/scripts/__init__.py | 0 backend/scripts/start_dev.sh | 2 +- changelog/2026-04-12_troubleshooting.md | 35 +++++++++++++++++ 6 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 backend/alembic/versions/38efeff7c45a_create_users_table.py create mode 100644 backend/scripts/__init__.py create mode 100644 changelog/2026-04-12_troubleshooting.md diff --git a/backend/alembic/versions/38efeff7c45a_create_users_table.py b/backend/alembic/versions/38efeff7c45a_create_users_table.py new file mode 100644 index 0000000..ccd3b2c --- /dev/null +++ b/backend/alembic/versions/38efeff7c45a_create_users_table.py @@ -0,0 +1,39 @@ +"""create users table + +Revision ID: 38efeff7c45a +Revises: +Create Date: 2026-04-12 14:00:30.503479 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +revision: str = '38efeff7c45a' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.String(), nullable=False), + sa.Column('email', sa.String(), nullable=False), + sa.Column('hashed_password', sa.String(), nullable=False), + sa.Column('full_name', sa.String(), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=False), + sa.Column('is_superuser', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_users_email'), table_name='users') + op.drop_table('users') + # ### end Alembic commands ### diff --git a/backend/app/core/security.py b/backend/app/core/security.py index 3480840..4eae24c 100644 --- a/backend/app/core/security.py +++ b/backend/app/core/security.py @@ -1,19 +1,17 @@ from datetime import datetime, timedelta, timezone +import bcrypt from jose import jwt -from passlib.context import CryptContext from app.core.config import settings -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") - def hash_password(password: str) -> str: - return pwd_context.hash(password) + return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() def verify_password(plain: str, hashed: str) -> bool: - return pwd_context.verify(plain, hashed) + return bcrypt.checkpw(plain.encode(), hashed.encode()) def create_access_token(subject: str) -> str: diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 9b0730d..b866301 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "pydantic[email]>=2.7", "pydantic-settings>=2.2", "python-jose[cryptography]>=3.3", - "passlib[bcrypt]>=1.7", + "bcrypt>=4.0", "python-multipart>=0.0.9", ] diff --git a/backend/scripts/__init__.py b/backend/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/scripts/start_dev.sh b/backend/scripts/start_dev.sh index 288d9e7..6b40d51 100755 --- a/backend/scripts/start_dev.sh +++ b/backend/scripts/start_dev.sh @@ -5,7 +5,7 @@ echo "[start] running migrations..." alembic upgrade head echo "[start] seeding dev data..." -python scripts/seed.py +python -m scripts.seed echo "[start] starting uvicorn..." exec uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload diff --git a/changelog/2026-04-12_troubleshooting.md b/changelog/2026-04-12_troubleshooting.md new file mode 100644 index 0000000..71adc4b --- /dev/null +++ b/changelog/2026-04-12_troubleshooting.md @@ -0,0 +1,35 @@ +# 2026-04-12 — Troubleshoot dev stack startup + +**Timestamp:** 2026-04-12T15:30:00 + +## Summary + +Fixed three startup failures discovered when running the dev stack for the first time. + +## Issues Fixed + +1. **`ModuleNotFoundError: No module named 'app'`** — `python scripts/seed.py` does not add the working directory to `sys.path`. Changed to `python -m scripts.seed` which uses module mode and adds `/app` to the path. Added `backend/scripts/__init__.py` to make the directory a package. + +2. **`relation "users" does not exist`** — No Alembic migration files existed, so `alembic upgrade head` was a no-op. Generated the initial migration (`38efeff7c45a_create_users_table.py`) by running `alembic revision --autogenerate` inside the backend container against the live database. + +3. **`ValueError: password cannot be longer than 72 bytes` (passlib + bcrypt 4.x incompatibility)** — `passlib` is unmaintained and its bcrypt wrap-bug detection raises an exception against bcrypt 4.x. Replaced `passlib[bcrypt]` with direct `bcrypt>=4.0` usage in both `pyproject.toml` and `app/core/security.py`. + +## Smoke Test Results (all passing) + +- `GET /api/health` → `{"status":"ok"}` +- `POST /api/auth/login` → JWT token issued for `test@example.com` +- `GET /api/users/me` → returns user profile with valid token +- `POST /api/auth/register` → new user created successfully +- Weak password (`"password"`) → rejected with detailed validation errors +- Frontend `http://localhost:5173` → HTTP 200 + +## Files Added + +- `backend/scripts/__init__.py` — makes scripts/ a Python package +- `backend/alembic/versions/38efeff7c45a_create_users_table.py` — initial migration + +## Files Modified + +- `backend/scripts/start_dev.sh` — `python scripts/seed.py` → `python -m scripts.seed` +- `backend/pyproject.toml` — `passlib[bcrypt]>=1.7` → `bcrypt>=4.0` +- `backend/app/core/security.py` — replaced passlib with direct bcrypt calls