728x90
1. auth.py 코드 정리
import sys
sys.path.append('..')
from starlette.responses import RedirectResponse
from fastapi import Depends, HTTPException, status, APIRouter, Request, Response, Form
from typing import Optional
import models
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from database import SessionLocal, engine
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
from datetime import datetime, timedelta
from jose import jwt, JWTError
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
SECRET_KEY = "{SECRET_KEY}"
ALGORITHM = "HS256"
templates = Jinja2Templates(directory="templates")
bcrypt_context = CryptContext(schemes=['bcrypt'], deprecated="auto")
models.Base.metadata.create_all(bind=engine)
oauth2_bearer = OAuth2PasswordBearer(tokenUrl="token")
router = APIRouter(
prefix="/auth",
tags=["auth"],
responses={401: {"user": "Not authorized"}}
)
class LoginForm:
def __init__(self, request: Request):
self.request: Request = request
self.username: Optional[str] = None
self.password: Optional[str] = None
async def create_oauth_form(self):
form = await self.request.form()
self.username = form.get("email")
self.password = form.get("password")
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
def get_password_hash(password):
return bcrypt_context.hash(password)
def verify_password(plain_password, hashed_password):
return bcrypt_context.verify(plain_password, hashed_password)
def authenticate_user(username: str, password: str, db):
user = db.query(models.Users).filter(models.Users.username == username).first()
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(username: str, user_id: int, expires_delta: Optional[timedelta] = None):
encode = {"sub": username, "id": user_id}
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
encode.update({"exp": expire})
return jwt.encode(encode, SECRET_KEY, algorithm=ALGORITHM)
async def get_current_user(request: Request):
try:
token = request.cookies.get("access_token")
if token is None:
return None
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
user_id: int = payload.get("id")
if username is None or user_id is None:
logout(request)
return {"username": username, "id": user_id}
except JWTError:
return HTTPException(status_code=404, detail="Not Found")
@router.get("/", response_class=HTMLResponse)
async def authentication_page(request: Request):
return templates.TemplateResponse("login.html", {"request":request})
@router.post("/", response_class=HTMLResponse)
async def login(request: Request, db: Session = Depends(get_db)):
try:
form = LoginForm(request)
await form.create_oauth_form()
response = RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
validate_user_cookie = await login_for_access_token(response=response, form_data=form, db=db)
if not validate_user_cookie:
msg = "Incorrect Username or Password"
return templates.TemplateResponse("login.html", {"request": request, "msg": msg})
return response
except HTTPException:
msg = "Unknown Error"
return templates.TemplateResponse("login.html", {"request":request, "msg": msg})
@router.get("/logout")
async def logout(request: Request):
msg = "logout Successful"
response = templates.TemplateResponse("login.html", {"request":request, "msg":msg})
response.delete_cookie(key="access_token")
return response
@router.get("/register", response_class=HTMLResponse)
async def register(request:Request):
return templates.TemplateResponse("register.html", {"request":request})
@router.post("/register", response_class=HTMLResponse)
async def register_user(request: Request, email: str = Form(...), username: str = Form(...),
firstname: str = Form(...), lastname: str = Form(...),
password: str = Form(...), password2: str = Form(...),
db: Session = Depends(get_db)):
validation1 = db.query(models.Users).filter(models.Users.username == username).first()
validation2 = db.query(models.Users).filter(models.Users.email == email).first()
if password != password2 or validation1 is not None or validation2 is not None:
msg = "Invalid register request"
return templates.TemplateResponse("register.html", {"request": request, "msg": msg})
user_model = models.Users()
user_model.username = username
user_model.email = email
user_model.first_name = firstname
user_model.last_name = lastname
user_model.hashed_password = get_password_hash(password)
user_model.is_active = True
db.add(user_model)
db.flush()
db.commit()
msg = "User Successfully created"
return templates.TemplateResponse("login.html", {"request": request, "msg": msg})
@router.post("/token")
async def login_for_access_token(response: Response, form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_db)):
user = authenticate_user(form_data.username, form_data.password, db)
if not user:
return False
token = create_access_token(user.username,
user.id,
expires_delta = timedelta(minutes=60))
response.set_cookie(key="access_token", value=token, httponly=True)
return True
2. todos.py 코드 정리
import sys
sys.path.append("..")
from todoapp.routers.auth import get_current_user
from starlette.responses import RedirectResponse
from starlette import status
from sqlalchemy.orm import Session
from fastapi import APIRouter, Request, Depends, Form
import models
from database import engine, SessionLocal
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
router = APIRouter(
prefix="/todos",
tags=['todos'],
responses={404: {"description": "Not found"}}
)
models.Base.metadata.create_all(bind=engine)
templates = Jinja2Templates(directory="templates")
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.get("/", response_class=HTMLResponse)
async def read_all_by_user(request: Request, db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
todos = db.query(models.Todos).filter(models.Todos.owner_id == user.get("id")).all()
return templates.TemplateResponse("home.html", {"request":request, "todos":todos, "user": user})
@router.get("/add-todo", response_class=HTMLResponse)
async def add_new_todo(request: Request):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
return templates.TemplateResponse("add-todo.html", {"request":request, "user": user})
@router.post("/add-todo", response_class=HTMLResponse)
async def create_todo(request: Request, title: str = Form(...), description: str =Form(...),
priority: int = Form(...), db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
todo_model = models.Todos()
todo_model.title = title
todo_model.description = description
todo_model.priority = priority
todo_model.complete = False
todo_model.owner_id = user.get("id")
db.add(todo_model)
db.flush()
db.commit()
return RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
@router.get("/edit-todo/{todo_id}", response_class=HTMLResponse)
async def edit_todo(request: Request, todo_id: int, db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
todo = db.query(models.Todos).filter(models.Todos.id == todo_id).first()
return templates.TemplateResponse("edit-todo.html", {"request":request, "todo": todo, "user": user})
@router.post("/edit-todo/{todo_id}", response_class=HTMLResponse)
async def edit_todo_commit(request: Request, todo_id: int, title: str = Form(...),
description = Form(...), priority: int = Form(...),
db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
todo_model = db.query(models.Todos).filter(models.Todos.id == todo_id).first()
todo_model.title = title
todo_model.description = description
todo_model.priority = priority
db.add(todo_model)
db.flush()
db.commit()
return RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
@router.get("/delete/{todo_id}")
async def delete_todo(request: Request, todo_id: int, db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
todo_model = db.query(models.Todos).filter(models.Todos.id == todo_id)\
.filter(models.Todos.owner_id == user.get("id")).first()
if todo_model is None:
return RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
db.query(models.Todos).filter(models.Todos.id == todo_id).delete()
db.commit()
return RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
@router.get("/complete/{todo_id}", response_class=HTMLResponse)
async def complete_todo(request: Request, todo_id: int, db: Session = Depends(get_db)):
user = await get_current_user(request)
if user is None:
return RedirectResponse(url="/auth", status_code=status.HTTP_302_FOUND)
todo = db.query(models.Todos).filter(models.Todos.id == todo_id).first()
todo.complete = not todo.complete
db.add(todo)
db.flush()
db.commit()
return RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
3. main.py 코드 정리
from fastapi import FastAPI
import models
from database import engine
from routers import auth, todos
from starlette.staticfiles import StaticFiles
from starlette import status
from starlette.responses import RedirectResponse
app = FastAPI()
models.Base.metadata.create_all(bind=engine)
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
async def root():
return RedirectResponse(url="/todos", status_code=status.HTTP_302_FOUND)
app.include_router(auth.router)
app.include_router(todos.router)
4. navbar 코드 정리
<div>
<nav class="navbar navbar-expand-md navbar-dark main-color fixed-top">
<a class="navbar-brand" href="#">Todo App</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
{% if user %}
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav ml-auto">
{% if user %}
<li class="nav-item m-1">
<a type="button" class="btn btn-outline-light" href="/auth/logout">Logout</a>
</li>
{% endif %}
</ul>
</div>
</nav>
</div>
5. 디렉터리 수정
fastapi | |||||
main.py database.py models.py todos.db |
templates | static | routers | ||
todo | |||||
css | js | ||||
home.html add-todo.html edit-todo.html login.html register.html layout.html navbar.html |
base.css bootstrap.css |
bootstrap.js jquery-slim.js popper.js |
auth.py todos.py |
728x90
SMALL
'Develop > FastAPI' 카테고리의 다른 글
FastAPI 프로젝트 git 올리기(git 명령어) - 96 (0) | 2023.02.01 |
---|---|
FastAPI 프로젝트 진행(비밀번호 변경, 라우터 추가) - 95 (0) | 2023.01.31 |
FastAPI 프로젝트 진행(회원가입 기능 구현) - 93 (0) | 2023.01.29 |
FastAPI 프로젝트 진행(로그아웃 기능 구현) - 92 (0) | 2023.01.29 |
FastAPI 프로젝트 진행(로그인 추가, 쿠키 세션) - 91 (0) | 2023.01.29 |