first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:25:19 +03:00
commit 361dbef019
25 changed files with 814 additions and 0 deletions

34
app/core/config.py Normal file
View File

@@ -0,0 +1,34 @@
from __future__ import annotations
from pydantic_settings import BaseSettings
from pydantic import Field
from dotenv import load_dotenv
load_dotenv()
class Settings(BaseSettings):
model_config = {
"env_file": ".env",
"extra": "allow",
}
# Read DATABASE_URL from environment via model_config; avoid using Field(..., env=...)
DATABASE_URL: str = "sqlite:///./dev.db"
JWT_SECRET: str
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 15
REFRESH_TOKEN_EXPIRE_DAYS: int = 30
GOOGLE_CLIENT_ID: str | None = None
GOOGLE_CLIENT_SECRET: str | None = None
GOOGLE_REDIRECT_URL: str | None = None
GITHUB_CLIENT_ID: str | None = None
GITHUB_CLIENT_SECRET: str | None = None
GITHUB_REDIRECT_URL: str | None = None
SERVER_NAME: str = "localhost:8000"
DEBUG: bool = True
settings = Settings()

27
app/core/oauth.py Normal file
View File

@@ -0,0 +1,27 @@
from urllib.parse import urlencode
from typing import Dict
from app.core.config import settings
def google_authorize_url(state: str = "state") -> str:
params = {
"client_id": settings.GOOGLE_CLIENT_ID,
"redirect_uri": settings.GOOGLE_REDIRECT_URL,
"response_type": "code",
"scope": "openid email profile",
"state": state,
"access_type": "offline",
"prompt": "consent",
}
return "https://accounts.google.com/o/oauth2/v2/auth?" + urlencode(params)
def github_authorize_url(state: str = "state") -> str:
params = {
"client_id": settings.GITHUB_CLIENT_ID,
"redirect_uri": settings.GITHUB_REDIRECT_URL,
"scope": "user:email",
"state": state,
}
return "https://github.com/login/oauth/authorize?" + urlencode(params)

38
app/core/security.py Normal file
View File

@@ -0,0 +1,38 @@
from datetime import datetime, timedelta, timezone
from typing import Tuple
import jwt
from argon2 import PasswordHasher
from app.core.config import settings
ph = PasswordHasher()
def hash_password(password: str) -> str:
return ph.hash(password)
def verify_password(hash: str, password: str) -> bool:
try:
return ph.verify(hash, password)
except Exception:
return False
def create_access_token(sub: str) -> Tuple[str, datetime]:
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
payload = {"sub": sub, "exp": expire}
token = jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.ALGORITHM)
return token, expire
def create_refresh_token(sub: str) -> Tuple[str, datetime]:
expire = datetime.now(timezone.utc) + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
payload = {"sub": sub, "exp": expire}
token = jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.ALGORITHM)
return token, expire
def decode_token(token: str) -> dict:
return jwt.decode(token, settings.JWT_SECRET, algorithms=[settings.ALGORITHM])