-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor api #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Refactor api #2
Changes from all commits
3308520
eb66ee2
f5e7cf8
7e975d2
8b07f14
74a85b1
a5a4760
72b9182
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
app/__pycache__ | ||
app/.idea | ||
app/.venv | ||
app/orm-user.db | ||
app/app/__pycache__ | ||
app/app/api/__pycache__ | ||
app/app/auth/__pycache__ | ||
app/app/core/__pycache__ | ||
app/app/db/__pycache__ | ||
app/app/models/__pycache__ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets add linting to the project, have a look at: https://github.com/astral-sh/ruff |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from .user import router as user_router | ||
from .login import router as login_router | ||
|
||
__all__ = ["user_router", "login_router"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from fastapi import APIRouter, Depends, HTTPException, status | ||
from fastapi.security import OAuth2PasswordRequestForm | ||
from sqlmodel import Session | ||
from app.auth.auth import authenticate_user | ||
from app.auth.security import create_access_token | ||
from app.db.session import get_session | ||
from datetime import timedelta | ||
from app.core.config import ACCESS_TOKEN_EXPIRE_MINUTES | ||
|
||
router = APIRouter() | ||
|
||
@router.post("/login") | ||
async def login(form_data: OAuth2PasswordRequestForm = Depends(), session: Session = Depends(get_session)): | ||
user = authenticate_user(form_data.username, session, form_data.password) | ||
if not user: | ||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password") | ||
access_token = create_access_token(data={"sub": user.username}, expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)) | ||
return {"access_token": access_token, "token_type": "bearer"} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from fastapi import APIRouter, Depends | ||
from app.models.user import User | ||
from app.auth.auth import get_current_active_user | ||
|
||
router = APIRouter() | ||
|
||
@router.get("/users/me", response_model=User) | ||
async def read_users_me(current_user: User = Depends(get_current_active_user)): | ||
return current_user |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from sqlmodel import Session, select | ||
from app.models.user import User | ||
from app.db.session import get_session | ||
from app.auth.security import get_hash | ||
import random | ||
|
||
router = APIRouter() | ||
|
||
@router.post("/signup") | ||
async def add_user(session: Session = Depends(get_session), name: str = "", password: str = "", email: str = ""): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend using Pydantic models (e.g., BaseModel classes) for the request body instead of passing parameters like name, email, and password directly in the function signature. |
||
statement = select(User).where(User.email == email) | ||
if session.exec(statement).first(): | ||
raise HTTPException(status_code=400, detail="Email already exists") | ||
|
||
while True: | ||
username = name + str(random.randint(1, 100)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use slugify instead. |
||
if not session.get(User, username): | ||
break | ||
|
||
new_user = User( | ||
name=name, | ||
username=username, | ||
email=email, | ||
hashed_password=get_hash(password), | ||
disabled=False | ||
) | ||
session.add(new_user) | ||
session.commit() | ||
session.refresh(new_user) | ||
return {"name": name, "username": username} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from fastapi import HTTPException, status, Depends | ||
from jose import jwt, JWTError | ||
from sqlmodel import Session | ||
from fastapi.security import OAuth2PasswordBearer | ||
from app.models.user import User | ||
from app.db.session import get_session | ||
from app.auth.security import verify_password | ||
from app.core.config import SECRET_KEY, ALGORITHM | ||
|
||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") | ||
|
||
def authenticate_user(username: str, session: Session, password: str): | ||
user = session.get(User, username) | ||
if not user or not verify_password(user.hashed_password, password): | ||
return False | ||
return user | ||
|
||
async def get_current_user(token: str = Depends(oauth2_scheme), session: Session = Depends(get_session)): | ||
credentials_exception = HTTPException( | ||
status_code=status.HTTP_401_UNAUTHORIZED, | ||
detail="Could not validate credentials", | ||
headers={"WWW-Authenticate": "Bearer"}, | ||
) | ||
try: | ||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) | ||
username: str = payload.get("sub") | ||
if username is None: | ||
raise credentials_exception | ||
except JWTError: | ||
raise credentials_exception | ||
|
||
user = session.get(User, username) | ||
if user is None: | ||
raise credentials_exception | ||
return user | ||
|
||
async def get_current_active_user(current_user: User = Depends(get_current_user)): | ||
if current_user.disabled: | ||
raise HTTPException(status_code=400, detail="Inactive user") | ||
return current_user |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from passlib.context import CryptContext | ||
from datetime import datetime, timedelta, timezone | ||
from jose import jwt | ||
from app.core.config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES | ||
|
||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") | ||
|
||
def get_hash(password: str) -> str: | ||
return pwd_context.hash(password) | ||
|
||
def verify_password(hashed_password, password): | ||
return pwd_context.verify(password, hashed_password) | ||
|
||
def create_access_token(data: dict, expires_delta: timedelta | None = None): | ||
to_encode = data.copy() | ||
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)) | ||
to_encode.update({"exp": expire}) | ||
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
SECRET_KEY = "your_secret_key_here" | ||
ALGORITHM = "HS256" | ||
ACCESS_TOKEN_EXPIRE_MINUTES = 30 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from sqlmodel import SQLModel | ||
from .session import engine | ||
|
||
def create_db_and_table(): | ||
SQLModel.metadata.create_all(engine) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from sqlmodel import create_engine, Session | ||
|
||
connect_args = {"check_same_thread": False} | ||
engine = create_engine("sqlite:///orm-user.db", connect_args=connect_args) | ||
|
||
def get_session(): | ||
with Session(engine) as session: | ||
yield session |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .user import User | ||
__all__ = ["User"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from sqlmodel import SQLModel, Field | ||
|
||
class User(SQLModel, table=True): | ||
name: str | ||
username: str | None = Field(default=None, primary_key=True) | ||
email: str | None = Field(default=None) | ||
hashed_password: str | None = Field(default=None) | ||
disabled: bool | None = Field(default=None) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from fastapi import FastAPI | ||
from app.api.routes import user, login, profile | ||
from app.db.init_db import create_db_and_table | ||
|
||
app = FastAPI() | ||
app.include_router(profile.router, tags=["Auth Profile"]) | ||
app.include_router(user.router, tags=["Users"]) | ||
app.include_router(login.router, tags=["Auth"]) | ||
|
||
@app.on_event("startup") | ||
def on_startup(): | ||
create_db_and_table() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
annotated-types==0.7.0 | ||
anyio==4.9.0 | ||
bcrypt==3.2.2 | ||
certifi==2025.6.15 | ||
cffi==1.17.1 | ||
click==8.2.1 | ||
ecdsa==0.19.1 | ||
fastapi==0.115.12 | ||
greenlet==3.2.2 | ||
h11==0.16.0 | ||
httpcore==1.0.9 | ||
httpx==0.28.1 | ||
idna==3.10 | ||
iniconfig==2.1.0 | ||
packaging==25.0 | ||
passlib==1.7.4 | ||
pluggy==1.6.0 | ||
pyasn1==0.4.8 | ||
pycparser==2.22 | ||
pydantic==2.11.5 | ||
pydantic_core==2.33.2 | ||
Pygments==2.19.1 | ||
pytest==8.4.0 | ||
pytest-asyncio==1.0.0 | ||
python-jose==3.4.0 | ||
python-multipart==0.0.20 | ||
rsa==4.9.1 | ||
six==1.17.0 | ||
sniffio==1.3.1 | ||
SQLAlchemy==2.0.41 | ||
sqlmodel==0.0.24 | ||
starlette==0.46.2 | ||
typing-inspection==0.4.1 | ||
typing_extensions==4.13.2 | ||
uvicorn==0.34.2 |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do
**/__pycache__/
instead.