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:
64
app/database.py
Normal file
64
app/database.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
Database engine and session factory.
|
||||
|
||||
We use SQLAlchemy 2.x with the "declarative" ORM style. All models should
|
||||
inherit from ``Base`` defined here.
|
||||
|
||||
Session lifecycle:
|
||||
- ``get_db()`` is a FastAPI dependency that opens a session per request and
|
||||
guarantees it is closed (and rolled back on error) when the request ends.
|
||||
"""
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import DeclarativeBase, sessionmaker
|
||||
|
||||
from app.config import settings
|
||||
|
||||
|
||||
# connect_args is SQLite-specific: it allows the same connection to be used
|
||||
# from multiple threads, which is required because FastAPI runs in a thread
|
||||
# pool. This flag is harmless / unused for PostgreSQL.
|
||||
_connect_args = (
|
||||
{"check_same_thread": False}
|
||||
if settings.database_url.startswith("sqlite")
|
||||
else {}
|
||||
)
|
||||
|
||||
engine = create_engine(settings.database_url, connect_args=_connect_args)
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""All ORM models inherit from this class."""
|
||||
pass
|
||||
|
||||
|
||||
def get_db():
|
||||
"""
|
||||
FastAPI dependency that yields a database session.
|
||||
|
||||
Usage::
|
||||
|
||||
@router.get("/things")
|
||||
def list_things(db: Session = Depends(get_db)):
|
||||
...
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def create_tables():
|
||||
"""Create all tables that are registered on Base.metadata.
|
||||
|
||||
Called once at application startup (see main.py). Safe to run multiple
|
||||
times – SQLAlchemy will not recreate existing tables.
|
||||
"""
|
||||
# Importing models here ensures they are registered on Base.metadata
|
||||
# before create_all() is called.
|
||||
import app.models.user # noqa: F401
|
||||
import app.models.item # noqa: F401
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
Reference in New Issue
Block a user