- revision="0003", down_revision="0002"
- upgrade(): collects null-user object_keys, deletes document_topics cascade,
deletes null-user documents, removes MinIO objects (skip if MINIO_ENDPOINT unset),
deletes all topics (D-10), alters documents.user_id NOT NULL via batch_alter_table,
creates ix_topics_user_id index, reconciles quotas.used_bytes from SUM(size_bytes)
- downgrade(): drops ix_topics_user_id, reverts user_id to nullable; documents not restored
- batch_alter_table ensures SQLite compatibility for test suite
- MinIO step gated on MINIO_ENDPOINT env var for safe SQLite test runs
- Add BackupCode model to db/models.py with user_id FK, code_hash (Argon2), used_at (nullable)
- Add ix_backup_codes_user_id index on backup_codes.user_id
- Add password_must_change BOOLEAN NOT NULL DEFAULT false to User model (ADMIN-01)
- Extend config.py Settings with JWT, SMTP, admin bootstrap, and CORS fields (D-01, D-04, D-09)
- Add env_list_separator=',' for cors_origins env var parsing
- Append PyJWT, pwdlib[argon2], pyotp, aioredis, slowapi to requirements.txt
- Add .env.example entries for SECRET_KEY, ADMIN_EMAIL, SMTP_*, CORS_ORIGINS
- Create migration 0002 adding backup_codes table and password_must_change column
- Add TDD tests for all Task 1 acceptance criteria (7 tests pass)
- backend/alembic.ini: script_location=migrations, sqlalchemy.url=%(DATABASE_MIGRATE_URL)s
- backend/migrations/env.py: async_engine_from_config + Base.metadata wiring;
runtime os.environ.get("DATABASE_MIGRATE_URL") injection (alembic.ini interpolation
does not read OS env directly)
- backend/migrations/versions/0001_initial_schema.py: creates all 11 tables in
dependency order with correct FKs, indexes, and named constraints
- documents.user_id is nullable=True per D-03; Phase 2 adds NOT NULL
- Ends with GRANT + ALTER DEFAULT PRIVILEGES for docuvault_app (Pitfall 4)
- Also grants USAGE/SELECT on sequences (audit_log.id autoincrement)
- downgrade() drops all tables in reverse dependency order