From d5fddb8fd2dfe8c7987cb2ea1b39843fd1d65d8a Mon Sep 17 00:00:00 2001 From: "Obi.M" Date: Thu, 13 Mar 2025 14:13:06 +0100 Subject: [PATCH] Initial commit from template --- .gitignore | 35 +++++++++++++++++++++++++++++++++++ core/auth.py | 9 +++++++++ core/database.py | 11 +++++++++++ core/router.py | 35 +++++++++++++++++++++++++++++++++++ endpoints/login.post.py | 25 +++++++++++++++++++++++++ endpoints/signup.post.py | 33 +++++++++++++++++++++++++++++++++ main.py | 18 ++++++++++++++++++ requirements.txt | 5 +++++ 8 files changed, 171 insertions(+) create mode 100644 .gitignore create mode 100644 core/auth.py create mode 100644 core/database.py create mode 100644 core/router.py create mode 100644 endpoints/login.post.py create mode 100644 endpoints/signup.post.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8765460 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class + +# Environment +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Logs +*.log + +# Local development +.settings/ +.vscode/ + +# Database +*.db +*.sqlite + +# Temporary files +*.tmp +*.temp + +# Security +secrets.json +credentials.json + +# IDE specific +.idea/ diff --git a/core/auth.py b/core/auth.py new file mode 100644 index 0000000..7f3d3cf --- /dev/null +++ b/core/auth.py @@ -0,0 +1,9 @@ +from fastapi import Depends, HTTPException +from core.database import fake_users_db + +async def get_current_user_dummy(username: str = "demo"): + """Demo auth dependency""" + user = fake_users_db.get(username) + if not user: + raise HTTPException(status_code=404, detail="User not found") + return user diff --git a/core/database.py b/core/database.py new file mode 100644 index 0000000..43c2986 --- /dev/null +++ b/core/database.py @@ -0,0 +1,11 @@ +from typing import Dict, Any + +# Demo database +fake_users_db: Dict[str, Dict[str, Any]] = { + "demo": { + "id": "7b0a5c28-32aa-4e39-975f-88c8a029a5e2", + "email": "demo@example.com", + "password": "password", + "disabled": False + } +} diff --git a/core/router.py b/core/router.py new file mode 100644 index 0000000..0c582df --- /dev/null +++ b/core/router.py @@ -0,0 +1,35 @@ +import importlib.util +from pathlib import Path +from fastapi import APIRouter + +def load_endpoints(base_path: Path = Path("endpoints")) -> APIRouter: + router = APIRouter() + + for file_path in base_path.glob("**/*.*.py"): + # Parse path components + relative_path = file_path.relative_to(base_path) + path_parts = list(relative_path.parts) + + # Extract HTTP method from filename + filename = path_parts[-1] + name_parts = filename.split(".") + if len(name_parts) < 3 or name_parts[-1] != "py": + continue # Skip invalid formats + + method = name_parts[-2].upper() + endpoint_name = ".".join(name_parts[:-2]) + + # Build URL path + url_path = "/" + "/".join(path_parts[:-1] + [endpoint_name]) + url_path = url_path.replace("[", "{").replace("]", "}") # Convert path params + + # Load the module + spec = importlib.util.spec_from_file_location("", file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # Find the router in the module + if hasattr(module, "router"): + router.include_router(module.router, prefix=url_path) + + return router diff --git a/endpoints/login.post.py b/endpoints/login.post.py new file mode 100644 index 0000000..df5aa08 --- /dev/null +++ b/endpoints/login.post.py @@ -0,0 +1,25 @@ +from fastapi import APIRouter, Depends, HTTPException +from core.auth import get_current_user_dummy +from core.database import fake_users_db + +router = APIRouter() + +@router.post("/login") +async def login_demo( + username: str = "demo", + password: str = "password" +): + """Demo login endpoint""" + user = fake_users_db.get(username) + if not user or user["password"] != password: + raise HTTPException(status_code=400, detail="Invalid credentials") + + return { + "message": "Login successful (demo)", + "user": username, + "token": "dummy_jwt_token_123", + "features": { + "rate_limit": 100, + "expires_in": 3600 + } + } diff --git a/endpoints/signup.post.py b/endpoints/signup.post.py new file mode 100644 index 0000000..47f7fae --- /dev/null +++ b/endpoints/signup.post.py @@ -0,0 +1,33 @@ +from fastapi import APIRouter, HTTPException +from core.database import fake_users_db +import uuid + +router = APIRouter() + +@router.post("/signup") +async def signup_demo( + username: str = "new_user", + email: str = "user@example.com", + password: str = "securepassword123" +): + """Demo signup endpoint""" + if username in fake_users_db: + raise HTTPException(status_code=400, detail="Username already exists") + + user_id = str(uuid.uuid4()) + fake_users_db[username] = { + "id": user_id, + "email": email, + "password": password, + "disabled": False + } + + return { + "message": "User created successfully", + "user_id": user_id, + "username": username, + "next_steps": [ + "Verify your email (demo)", + "Complete profile setup" + ] + } diff --git a/main.py b/main.py new file mode 100644 index 0000000..5ef4955 --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +from fastapi import FastAPI +from pathlib import Path +from core.router import load_endpoints + +app = FastAPI(title="API Starter Template") + +# Load all endpoints +app.include_router(load_endpoints(Path("endpoints"))) + +@app.get("/") +def root(): + return { + "message": "API Server Running", + "endpoints": { + "login": "/auth/login (POST)", + "signup": "/auth/signup (POST)" + } + } diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..cc9fbcd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi>=0.68.0 +uvicorn[standard] +python-multipart +python-jose[cryptography] +passlib[bcrypt]