feat: initial FastAPI stub with auth, user management, and inventory CRUD

Sets up the complete application skeleton for Flipventory v2, an inventory
management tool for small/medium resellers using chaotic stockpiling.

Architecture
------------
- app/config.py         pydantic-settings; reads .env; holds admin_email guard
- app/database.py       SQLAlchemy 2.x engine, SessionLocal, Base, get_db dep
- app/models/user.py    User (email, bcrypt hash, is_admin bool, is_active)
- app/models/item.py    StorageBin + Item with FOUND/LISTED/SOLD/ARCHIVED enum
- app/schemas/          Pydantic request/response shapes (separate from models)
- app/auth/security.py  bcrypt password hashing + HS256 JWT creation/validation
- app/routers/auth.py   POST /auth/register, POST /auth/login, GET /auth/me
- app/routers/users.py  Admin-only CRUD for user accounts
- app/routers/items.py  /bins and /items CRUD with role-based access control

Key design decisions
--------------------
- martin@hohenberg.jp is ALWAYS admin: enforced on every login, cannot be
  demoted, cannot be deactivated. Prevents accidental lockout.
- All other registered e-mails are customers (is_admin=False).
- Customers can manage their own bins and items; admins see everything.
- Item hard-delete is admin-only; customers archive instead (audit safety).
- SQLite default for zero-infra local dev; any SQLAlchemy URL works in prod.
- Tables are created on startup via create_tables(); swap for Alembic later.

Addresses v1 Gitea issues
--------------------------
- Issue #1 (storage bins): StorageBin model + /bins CRUD endpoints
- Issue #2 (item CRUD): Item model + /items CRUD endpoints
- Issue #3 (audit logging): created_by_id / updated_by_id on every item row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Hohenberg
2026-06-16 10:40:53 +02:00
parent fe774c924f
commit 4e0d0d3962
19 changed files with 1332 additions and 0 deletions

45
app/config.py Normal file
View File

@@ -0,0 +1,45 @@
"""
Application-wide settings loaded from environment variables (or a .env file).
pydantic-settings reads the .env file automatically when BaseSettings is used,
so there is no need to call load_dotenv() manually.
"""
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
# -------------------------------------------------------------------------
# Auth / Security
# -------------------------------------------------------------------------
# Signing secret for JWT tokens. Must be kept private on the server.
secret_key: str = "insecure-dev-secret-replace-in-production"
# JWT signing algorithm. HS256 is a symmetric HMAC-SHA256 scheme which
# is perfectly fine for a single-server application.
algorithm: str = "HS256"
# Token lifetime in minutes. Tokens expire after this period and the
# user must log in again.
access_token_expire_minutes: int = 60
# -------------------------------------------------------------------------
# Database
# -------------------------------------------------------------------------
# Any SQLAlchemy-compatible URL. Defaults to a local SQLite file so the
# app works out-of-the-box without extra infrastructure.
database_url: str = "sqlite:///./flipventory.db"
# -------------------------------------------------------------------------
# Business rules
# -------------------------------------------------------------------------
# The e-mail address that is ALWAYS treated as the site-wide administrator.
# Even if the database row for this user somehow has admin=False, every
# login will force-set it back to True.
admin_email: str = "martin@hohenberg.jp"
class Config:
env_file = ".env"
# Module-level singleton import this everywhere instead of re-instantiating.
settings = Settings()