94 lines
3.0 KiB
Python
94 lines
3.0 KiB
Python
import os
|
|
import time
|
|
from fastapi import APIRouter, HTTPException, Depends, Request
|
|
from fastapi.responses import JSONResponse
|
|
from passlib.hash import bcrypt
|
|
import jwt
|
|
from . import models
|
|
|
|
router = APIRouter()
|
|
|
|
JWT_SECRET = os.getenv('LIFERPG_JWT_SECRET', 'dev_jwt_secret_change')
|
|
JWT_ALGO = 'HS256'
|
|
JWT_EXP_SECONDS = 60 * 60 * 24 # 1 day
|
|
|
|
|
|
def create_token(payload: dict) -> str:
|
|
now = int(time.time())
|
|
payload_out = {**payload, 'iat': now, 'exp': now + JWT_EXP_SECONDS}
|
|
return jwt.encode(payload_out, JWT_SECRET, algorithm=JWT_ALGO)
|
|
|
|
|
|
def decode_token(token: str) -> dict:
|
|
try:
|
|
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO])
|
|
except Exception:
|
|
return {}
|
|
|
|
|
|
@router.post('/signup')
|
|
def signup(payload: dict):
|
|
email = payload.get('email')
|
|
password = payload.get('password')
|
|
if not email or not password:
|
|
raise HTTPException(status_code=400, detail='email and password required')
|
|
db = models.SessionLocal()
|
|
try:
|
|
existing = db.query(models.User).filter_by(email=email).first()
|
|
if existing:
|
|
raise HTTPException(status_code=400, detail='email exists')
|
|
user = models.User(email=email, password_hash=bcrypt.hash(password), display_name=payload.get('display_name'))
|
|
db.add(user)
|
|
db.commit()
|
|
db.refresh(user)
|
|
token = create_token({'sub': user.id})
|
|
resp = JSONResponse({'id': user.id, 'email': user.email})
|
|
resp.set_cookie('session', token, httponly=True, secure=False, samesite='lax')
|
|
return resp
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post('/login')
|
|
def login(payload: dict):
|
|
email = payload.get('email')
|
|
password = payload.get('password')
|
|
if not email or not password:
|
|
raise HTTPException(status_code=400, detail='email and password required')
|
|
db = models.SessionLocal()
|
|
try:
|
|
user = db.query(models.User).filter_by(email=email).first()
|
|
if not user or not user.password_hash or not bcrypt.verify(password, user.password_hash):
|
|
raise HTTPException(status_code=401, detail='invalid credentials')
|
|
token = create_token({'sub': user.id})
|
|
resp = JSONResponse({'id': user.id, 'email': user.email})
|
|
resp.set_cookie('session', token, httponly=True, secure=False, samesite='lax')
|
|
return resp
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post('/logout')
|
|
def logout():
|
|
resp = JSONResponse({'ok': True})
|
|
resp.delete_cookie('session')
|
|
return resp
|
|
|
|
|
|
def get_current_user(request: Request):
|
|
token = request.cookies.get('session')
|
|
if not token:
|
|
raise HTTPException(status_code=401, detail='not authenticated')
|
|
data = decode_token(token)
|
|
uid = data.get('sub')
|
|
if not uid:
|
|
raise HTTPException(status_code=401, detail='invalid token')
|
|
db = models.SessionLocal()
|
|
try:
|
|
user = db.query(models.User).filter_by(id=uid).first()
|
|
if not user:
|
|
raise HTTPException(status_code=401, detail='user not found')
|
|
return user
|
|
finally:
|
|
db.close()
|