every changed processed

This commit is contained in:
bdrtr 2025-05-05 17:47:53 +03:00
parent 8cefa60d3a
commit 9e64832ba0
6 changed files with 138 additions and 135 deletions

View file

@ -1,26 +1,27 @@
from enum import Enum from enum import Enum
from backend.config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES from backend.config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
from backend.config import pwd_context from backend.config import pwd_context, get_session_db
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pydantic import BaseModel from pydantic import BaseModel
from fastapi import Depends, HTTPException from fastapi import Depends, HTTPException
from typing import Annotated from typing import Annotated, Optional
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
import jwt import jwt
from sqlmodel import SQLModel, Field, Session, select
from pydantic.networks import EmailStr
### TOKEN MODELLERİ ###
class Token(BaseModel): class Token(BaseModel):
access_token : str access_token: str
token_type : str token_type: str
class TokenData(BaseModel): class TokenData(BaseModel):
username : str | None = None username: Optional[str] = None
role : str | None = None role: Optional[str] = None
status : str | None = None status: Optional[str] = None
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login") ### ENUMS ###
#### ENUMS ####
class Role(str, Enum): class Role(str, Enum):
user = "user" user = "user"
admin = "admin" admin = "admin"
@ -32,46 +33,38 @@ class Status(str, Enum):
banned = "banned" banned = "banned"
suspended = "suspended" suspended = "suspended"
class User(BaseModel): ### KULLANICI MODELLERİ ###
username : str | None = None class UserBase(SQLModel):
user_id : int | None = None username: Optional[str] = None
role : Role | None = None user_id: Optional[int] = None
status : Status | None = None role: Optional[Role] = None
status: Optional[Status] = None
class UserInDb(UserBase):
hashed_password: str | None = None
class UserPublic(UserBase):
pass
class UserCreate(BaseModel):
username: Optional[str] = None
role: Optional[Role] = None
email : EmailStr | None = None
status: Optional[Status] = None
password : str | None = None
### VERİTABANI MODELİ ###
class DBUser(SQLModel, table=True):
__tablename__ = "users" # opsiyonel, sqlmodel bunu otomatik de atar
user_id: Optional[int] = Field(default=None, primary_key=True)
username: str = Field(index=True, nullable=False)
hashed_password: str = Field(nullable=False)
role: Role = Field(default=Role.user)
status: Status = Field(default=Status.active)
class UserInDb(User): ### AUTH ###
hashed_password : str | None = None oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
class UserPublic(BaseModel):
username : str | None = None
role : Role | None = None
status : Status | None = None
user_id : int | None = None
fake_db = {
"bedir": {
"username": "bedir",
"user_id": 1,
"hashed_password": "$2a$12$mYGWGo9c3Di3SJyYjYf3XOAsu5nP8jekf3KTItO9pbUBEm5BcapRO", # Bcrypt örneği
"role": Role.user,
"status": Status.active,
},
"alice": {
"username": "alice",
"user_id": 2,
"hashed_password": "$2b$12$Alic3FakeHashedPasSw0rdxxxxxxxyyyyyyzzzzzz",
"role": Role.user,
"status": Status.suspended,
},
"adminuser": {
"username": "adminuser",
"user_id": 3,
"hashed_password": "$2b$12$AdminFakeHashedPasSw0rdxxxxxxxyyyyyyzzzzzz",
"role": Role.admin,
"status": Status.active,
}
}
def verify_password(plain_password: str, hashed_password: str) -> bool: def verify_password(plain_password: str, hashed_password: str) -> bool:
@ -80,18 +73,22 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
def get_password_hash(password: str) -> str: def get_password_hash(password: str) -> str:
return pwd_context.hash(password) return pwd_context.hash(password)
def authenticate_user(fake_db, username: str, password: str) -> UserInDb | bool: def authenticate_user(
user = fake_db.get(username) session: Annotated[Session, Depends(get_session_db)],
if not user: username: str,
return False password: str
if not verify_password(password, user["hashed_password"]): ) -> UserInDb | None:
return False
statement = select(DBUser).where(DBUser.username == username)
result = session.exec(statement).first()
if not result or not verify_password(password, result.hashed_password):
return None
return result
return user
def create_access_token( def create_access_token(
data : dict, data: dict,
expires_delta : Annotated[timedelta, None] = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES), expires_delta: Optional[timedelta] = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES),
) -> str: ) -> str:
to_encode = data.copy() to_encode = data.copy()
expire = datetime.now(timezone.utc) + expires_delta expire = datetime.now(timezone.utc) + expires_delta
@ -100,79 +97,46 @@ def create_access_token(
return encoded_jwt return encoded_jwt
def get_user(db, username: str) -> UserInDb | None: async def get_user(
if username in db: session: Annotated[Session, Depends(get_session_db)],
user_dict = db[username] username: str
return UserInDb(**user_dict) ) -> UserInDb | None:
return None
statement = select(DBUser).where(DBUser.username == username)
result = session.exec(statement).first()
return result
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
session: Annotated[Session, Depends(get_session_db)]
) -> UserPublic:
def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> UserPublic | None:
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=401, status_code=401,
detail="Burda bir hata var", detail="Invalid credentials",
headers={"WWW-Authenticate": "Bearer"}, headers={"WWW-Authenticate": "Bearer"},
) )
try: try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
token_data = TokenData(**payload) token_data = TokenData(**payload)
token_data.username = payload.get("sub") username: Optional[str] = payload.get("sub")
username : str = token_data.username
if username is None: if username is None:
raise credentials_exception raise credentials_exception
except jwt.PyJWTError: except jwt.PyJWTError:
raise credentials_exception raise credentials_exception
user = get_user(fake_db, username=username) user = await get_user(session, username)
if user is None: if user is None:
raise credentials_exception raise credentials_exception
return user return user
async def get_current_active_user( async def get_current_active_user(
current_user : Annotated[UserInDb, Depends(get_current_user)] current_user: Annotated[UserInDb, Depends(get_current_user)]
) -> UserPublic | None: ) -> UserPublic:
if current_user.status == Status.banned: if current_user.status == Status.banned:
raise HTTPException(status_code=400, detail="Inactive user") raise HTTPException(status_code=400, detail="Inactive user")
return current_user return current_user
"""
class User(BaseModel):
username : str
name : str | None = None
surname : str | None = None
email : EmailStr | None = None
role : Role | None = None
status : Status | None = None
bio : str | None = None
created_date : datetime | None = None
collections : list[str] | None = None
items = list[str] | None = None
class UserInDB(User):
hashed_password : str | None = None
class UserSelfProfile(BaseModel):
username : str
name : str | None = None
surname : str | None = None
email : EmailStr | None = None
role : Role | None = None
status : Status | None = None
bio : str | None = None
created_date : datetime | None = None
collections : list[str] | None = None
items = list[str] | None = None
class UserPublicProfile(BaseModel):
username : str
role : Role | None = None
bio : str | None = None
created_date : datetime | None = None
collections : list[str] | None = None
items = list[str] | None = None
"""

View file

@ -1,10 +1,15 @@
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from .models import UserInDb, User, Role, Token, UserPublic from .models import UserInDb, Role, Token, UserPublic, UserBase
from .models import get_current_active_user, authenticate_user, create_access_token , fake_db, get_current_user from .models import get_current_active_user, authenticate_user, create_access_token,get_current_user
from datetime import timedelta, datetime, timezone from datetime import timedelta, datetime, timezone
from ..auth.models import get_password_hash, verify_password
from ..config import ACCESS_TOKEN_EXPIRE_MINUTES from ..config import ACCESS_TOKEN_EXPIRE_MINUTES
from typing import Annotated, Optional from typing import Annotated, Optional
from sqlmodel import Session
from ..config import get_session_db
from fastapi import Depends
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from .models import UserCreate, DBUser
router = APIRouter( router = APIRouter(
@ -16,7 +21,7 @@ router = APIRouter(
@router.get("/me") @router.get("/me")
async def read_users_me( async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)], current_user: Annotated[UserBase, Depends(get_current_active_user)],
) -> UserPublic: ) -> UserPublic:
return current_user return current_user
@ -28,15 +33,16 @@ def ADMIN(current_user: Annotated[UserInDb, Depends(get_current_user)]):
@router.get('/home') @router.get('/home')
async def home(current_user : Annotated[User, Depends(ADMIN)]): async def home(current_user : Annotated[UserBase, Depends(ADMIN)]):
return {"message" : f"Welcome to home page {current_user.username}"} return {"message" : f"Welcome to home page {current_user.username}"}
@router.post('/login') @router.post('/login')
async def login_for_access_token( async def login_for_access_token(
form_data : Annotated[OAuth2PasswordRequestForm, Depends()], form_data : Annotated[OAuth2PasswordRequestForm, Depends()],
session : Annotated[Session, Depends(get_session_db)],
) -> Token: ) -> Token:
user = authenticate_user(fake_db, form_data.username, form_data.password) user = authenticate_user(session, form_data.username, form_data.password)
if not user: if not user:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
@ -45,6 +51,26 @@ async def login_for_access_token(
) )
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token( access_token = create_access_token(
data={"sub": user['username'], "role": user['role'], 'status': user['status']}, expires_delta=access_token_expires data={"sub": user.username, "role": user.role, 'status': user.status}, expires_delta=access_token_expires
) )
return Token(access_token=access_token, token_type="bearer") return Token(access_token=access_token, token_type="bearer")
@router.post('/register', response_model=UserPublic)
async def create_user(
session : Annotated[Session, Depends(get_session_db)],
user : Annotated[UserCreate, Depends()]
):
user_dict = user.dict()
print(user.password)
user_dict['hashed_password'] = get_password_hash(user.password)
print (user_dict['hashed_password'])
if not verify_password(user.password, user_dict['hashed_password']):
raise HTTPException(status_code=400, detail="Password hashing failed")
db_user = DBUser.model_validate(user_dict)
session.add(db_user)
session.commit()
session.refresh(db_user)
return db_user

View file

@ -4,18 +4,30 @@ from sqlalchemy.orm import sessionmaker
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from passlib.context import CryptContext from passlib.context import CryptContext
from sqlmodel import SQLModel, Field, Session
from dotenv import load_dotenv from dotenv import load_dotenv
import os import os
load_dotenv() load_dotenv()
Base = declarative_base() #basic class for declarative models # Veritabanı URL'sini oluştur
DATABASE_URL = (
f"postgresql://{os.getenv('USERNAME_DB')}:"
f"{os.getenv('PASSWORD_DB')}@"
f"{os.getenv('HOST_DB')}:"
f"{os.getenv('PORT_DB')}/"
f"{os.getenv('NAME_DB')}"
)
engine = create_engine(DATABASE_URL, echo=False)
def init_db():
SQLModel.metadata.create_all(engine)
def get_session_db():
with Session(engine) as session:
yield session
DATABASE_URL = f"postgresql://{os.getenv('USERNAME_DB')}:{os.getenv('PASSWORD_DB')}@{os.getenv('HOST_DB')}:{os.getenv('PORT_DB')}/{os.getenv('NAME_DB')}"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine)
Base.metadata.create_all(bind=engine)
### SECRET KEY ### ### SECRET KEY ###
@ -34,6 +46,9 @@ origins = [
] ]
app = FastAPI() app = FastAPI()
@app.on_event("startup")
def on_startup():
init_db()
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@ -42,3 +57,6 @@ app.add_middleware(
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],
) )

View file

@ -10,9 +10,3 @@ services:
ports: ports:
- "5434:5432" - "5434:5432"
volumes:
- postgres_data:/db
volumes:
postgres_data:
driver: local

View file

@ -1,7 +1,7 @@
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from ..auth.models import UserInDb from ..auth.models import UserBase
class UserProfile(UserInDb): class UserProfile(UserBase):
bio : str | None = None bio : str | None = None
created_date : datetime | None = None created_date : datetime | None = None
collections : list[str] | None = None collections : list[str] | None = None

View file

@ -30,6 +30,7 @@ rich-toolkit==0.14.4
shellingham==1.5.4 shellingham==1.5.4
sniffio==1.3.1 sniffio==1.3.1
SQLAlchemy==2.0.40 SQLAlchemy==2.0.40
sqlmodel==0.0.24
starlette==0.46.2 starlette==0.46.2
typer==0.15.3 typer==0.15.3
typing-inspection==0.4.0 typing-inspection==0.4.0