From 287a1e5629bfcdbd22e02b5d40bacbe5be028a0b Mon Sep 17 00:00:00 2001 From: carry <2641257231@qq.com> Date: Sun, 19 Jan 2025 23:45:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/__init__.py | 5 +++++ models/auth.py | 11 +++++++++++ models/database.py | 25 +++++++++++++++++++++++++ models/user.py | 25 +++++++++++++++++++++++++ requirements.txt | 8 ++++++++ 5 files changed, 74 insertions(+) create mode 100644 models/__init__.py create mode 100644 models/auth.py create mode 100644 models/database.py create mode 100644 models/user.py create mode 100644 requirements.txt diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..a1ec5f8 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,5 @@ +from .user import User +from .auth import get_password_hash, verify_password +from .database import init_db + +__all__ = ["User", "get_password_hash", "verify_password", "init_db"] diff --git a/models/auth.py b/models/auth.py new file mode 100644 index 0000000..9f22dee --- /dev/null +++ b/models/auth.py @@ -0,0 +1,11 @@ +from passlib.context import CryptContext + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +def get_password_hash(password: str) -> str: + """Generate password hash using bcrypt""" + return pwd_context.hash(password) + +def verify_password(plain_password: str, hashed_password: str) -> bool: + """Verify password against stored hash""" + return pwd_context.verify(plain_password, hashed_password) diff --git a/models/database.py b/models/database.py new file mode 100644 index 0000000..de2f5f9 --- /dev/null +++ b/models/database.py @@ -0,0 +1,25 @@ +from sqlalchemy.ext.asyncio import AsyncEngine +from .user import Base, User, UserRole +from sqlalchemy import select +from .auth import get_password_hash + +async def init_db(engine: AsyncEngine): + """Initialize database""" + async with engine.begin() as conn: + # Create all tables + await conn.run_sync(Base.metadata.create_all) + + # Check if system admin exists + result = await conn.execute( + select(User).where(User.role == UserRole.SYSTEM_ADMIN) + ) + if not result.scalars().first(): + # Create default system admin + admin = User( + username="admin", + password=get_password_hash("password"), + role=UserRole.SYSTEM_ADMIN, + description="default system admin" + ) + conn.add(admin) + await conn.commit() diff --git a/models/user.py b/models/user.py new file mode 100644 index 0000000..e36e3dd --- /dev/null +++ b/models/user.py @@ -0,0 +1,25 @@ +from datetime import datetime, timezone +from enum import Enum +from sqlalchemy import Column, Integer, String, Text, DateTime, Enum as SQLEnum +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() + +class UserRole(str, Enum): + SYSTEM_ADMIN = "system_admin" + ADMIN = "admin" + USER = "user" + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, autoincrement=True) + username = Column(String(50), unique=True, nullable=False) + password = Column(String(255), nullable=False) + role = Column(SQLEnum(UserRole), nullable=False, default=UserRole.USER) + description = Column(Text) + created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc)) + updated_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc)) + + def __repr__(self): + return f"" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f333860 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +fastapi>=0.95.2 +sqlalchemy>=2.0.15 +passlib>=1.7.4 +bcrypt>=4.0.1 +pyjwt>=2.6.0 +pytest>=7.3.1 +requests>=2.28.2 +uvicorn[standard]>=0.21.1