feat: Initialize frontend and backend structure with essential configurations
- Added TypeScript build info for frontend. - Created Vite configuration for React application. - Implemented pre-commit hook to run checks before commits. - Set up PostgreSQL Dockerfile with PostGIS support and initialization scripts. - Added database creation script for PostgreSQL with necessary extensions. - Established Python project configuration with dependencies and development tools. - Developed pre-commit script to enforce code quality checks for backend and frontend. - Created PowerShell script to set up Git hooks path.
This commit is contained in:
10
backend/app/api/__init__.py
Normal file
10
backend/app/api/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from backend.app.api.auth import router as auth_router
|
||||
from backend.app.api.health import router as health_router
|
||||
from backend.app.api.network import router as network_router
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(health_router, tags=["health"])
|
||||
router.include_router(auth_router)
|
||||
router.include_router(network_router)
|
||||
41
backend/app/api/auth.py
Normal file
41
backend/app/api/auth.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from backend.app.api.deps import get_current_user
|
||||
from backend.app.models import AuthResponse, LoginRequest, RegisterRequest, UserPublic
|
||||
from backend.app.services.auth import (
|
||||
authenticate_user,
|
||||
issue_token_for_user,
|
||||
register_user,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||
|
||||
|
||||
@router.post("/login", response_model=AuthResponse)
|
||||
async def login(credentials: LoginRequest) -> AuthResponse:
|
||||
user = authenticate_user(credentials.username, credentials.password)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
return issue_token_for_user(user)
|
||||
|
||||
|
||||
@router.post("/register", response_model=AuthResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def register(payload: RegisterRequest) -> AuthResponse:
|
||||
try:
|
||||
user = register_user(payload.username, payload.password, payload.full_name)
|
||||
except ValueError as exc:
|
||||
message = str(exc)
|
||||
status_code = status.HTTP_409_CONFLICT if "exists" in message else status.HTTP_400_BAD_REQUEST
|
||||
raise HTTPException(status_code=status_code, detail=message) from exc
|
||||
return issue_token_for_user(user)
|
||||
|
||||
|
||||
@router.get("/me", response_model=UserPublic)
|
||||
async def read_current_user(current_user: UserPublic = Depends(get_current_user)) -> UserPublic:
|
||||
return current_user
|
||||
40
backend/app/api/deps.py
Normal file
40
backend/app/api/deps.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from pydantic import ValidationError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.core.security import decode_access_token
|
||||
from backend.app.db.session import get_db_session
|
||||
from backend.app.models import TokenPayload, UserPublic
|
||||
from backend.app.services.auth import get_user, to_public_user
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login")
|
||||
|
||||
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)) -> UserPublic:
|
||||
try:
|
||||
payload = TokenPayload(**decode_access_token(token))
|
||||
except (ValueError, ValidationError) as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
) from exc
|
||||
|
||||
user = get_user(payload.sub)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User not found",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
return to_public_user(user)
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
yield from get_db_session()
|
||||
8
backend/app/api/health.py
Normal file
8
backend/app/api/health.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/health", summary="Service health status")
|
||||
async def health_check() -> dict[str, str]:
|
||||
return {"status": "ok"}
|
||||
16
backend/app/api/network.py
Normal file
16
backend/app/api/network.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.app.api.deps import get_current_user, get_db
|
||||
from backend.app.models import UserPublic
|
||||
from backend.app.services.network import get_network_snapshot
|
||||
|
||||
router = APIRouter(prefix="/network", tags=["network"])
|
||||
|
||||
|
||||
@router.get("", summary="Fetch a snapshot of the railway network")
|
||||
def read_network_snapshot(
|
||||
_: UserPublic = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> dict[str, list[dict[str, object]]]:
|
||||
return get_network_snapshot(db)
|
||||
Reference in New Issue
Block a user