Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- .gitignore +7 -1
- app/main.py +21 -2
- app/routers/auth/__init__.py +0 -0
- app/routers/auth/database.py +22 -0
- app/routers/auth/models.py +11 -0
- app/routers/auth/schemas.py +14 -0
- app/routers/auth/system.py +90 -0
- app/routers/auth/utils.py +29 -0
- app/routers/pdf/__init__.py +0 -0
- app/routers/pdf/pdf_tools.py +83 -0
- app/routers/security/__init__.py +0 -0
- app/routers/security/password_generator.py +44 -0
- app/routers/testers/help.py +5 -2
- app/routers/testers/server_status.py +73 -0
- requirements.txt +4 -1
- sql_app.db +0 -0
.gitignore
CHANGED
|
@@ -2,4 +2,10 @@
|
|
| 2 |
prompt.txt
|
| 3 |
|
| 4 |
#cache
|
| 5 |
-
__pycache__
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
prompt.txt
|
| 3 |
|
| 4 |
#cache
|
| 5 |
+
__pycache__
|
| 6 |
+
|
| 7 |
+
# documentations
|
| 8 |
+
llm_documentation.md
|
| 9 |
+
|
| 10 |
+
# enironment variable
|
| 11 |
+
.env
|
app/main.py
CHANGED
|
@@ -1,11 +1,25 @@
|
|
| 1 |
from fastapi import FastAPI
|
| 2 |
from fastapi.responses import HTMLResponse
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
# ==========================
|
| 5 |
# 📌 Image Module
|
| 6 |
# ==========================
|
| 7 |
from app.routers.image import jpgcompressor
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
# ==========================
|
| 11 |
# 🧪 Test Utilities
|
|
@@ -13,7 +27,7 @@ from app.routers.image import jpgcompressor
|
|
| 13 |
from app.routers.testers import random_number_generator
|
| 14 |
from app.routers.testers import help
|
| 15 |
from app.routers.testers import calculator
|
| 16 |
-
|
| 17 |
|
| 18 |
# ==========================
|
| 19 |
# 🚀 App Initialization
|
|
@@ -24,10 +38,15 @@ app = FastAPI()
|
|
| 24 |
# ==========================
|
| 25 |
# 🔗 Router Registration
|
| 26 |
# ==========================
|
|
|
|
| 27 |
app.include_router(jpgcompressor.router) # Image compression handler
|
|
|
|
| 28 |
app.include_router(random_number_generator.router) # Random number tester
|
| 29 |
app.include_router(help.router) # Helper/test info
|
| 30 |
app.include_router(calculator.router) # Mini calculator service
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
|
| 33 |
# ==========================
|
|
@@ -38,4 +57,4 @@ def greet_json():
|
|
| 38 |
return """
|
| 39 |
<h2>Go to the Swagger docs 👇</h2>
|
| 40 |
<a href="/docs">Click here for API Docs</a>
|
| 41 |
-
"""
|
|
|
|
| 1 |
from fastapi import FastAPI
|
| 2 |
from fastapi.responses import HTMLResponse
|
| 3 |
|
| 4 |
+
# ==========================
|
| 5 |
+
# 📄 PDF Module
|
| 6 |
+
# ==========================
|
| 7 |
+
from app.routers.pdf import pdf_tools
|
| 8 |
+
|
| 9 |
# ==========================
|
| 10 |
# 📌 Image Module
|
| 11 |
# ==========================
|
| 12 |
from app.routers.image import jpgcompressor
|
| 13 |
|
| 14 |
+
# ==========================
|
| 15 |
+
# 🔒 Security Module
|
| 16 |
+
# ==========================
|
| 17 |
+
from app.routers.security import password_generator
|
| 18 |
+
|
| 19 |
+
# ==========================
|
| 20 |
+
# 🛡️ Auth Module (NEW)
|
| 21 |
+
# ==========================
|
| 22 |
+
from app.routers.auth import system as auth_system
|
| 23 |
|
| 24 |
# ==========================
|
| 25 |
# 🧪 Test Utilities
|
|
|
|
| 27 |
from app.routers.testers import random_number_generator
|
| 28 |
from app.routers.testers import help
|
| 29 |
from app.routers.testers import calculator
|
| 30 |
+
from app.routers.testers import server_status # <-- NEW IMPORT
|
| 31 |
|
| 32 |
# ==========================
|
| 33 |
# 🚀 App Initialization
|
|
|
|
| 38 |
# ==========================
|
| 39 |
# 🔗 Router Registration
|
| 40 |
# ==========================
|
| 41 |
+
app.include_router(pdf_tools.router) # pdf manipulation
|
| 42 |
app.include_router(jpgcompressor.router) # Image compression handler
|
| 43 |
+
app.include_router(password_generator.router) # Generate a powerfull password that are harder to crack
|
| 44 |
app.include_router(random_number_generator.router) # Random number tester
|
| 45 |
app.include_router(help.router) # Helper/test info
|
| 46 |
app.include_router(calculator.router) # Mini calculator service
|
| 47 |
+
app.include_router(auth_system.router) # <-- NEW AUTH SYSTEM
|
| 48 |
+
app.include_router(server_status.router) # function to check the system
|
| 49 |
+
|
| 50 |
|
| 51 |
|
| 52 |
# ==========================
|
|
|
|
| 57 |
return """
|
| 58 |
<h2>Go to the Swagger docs 👇</h2>
|
| 59 |
<a href="/docs">Click here for API Docs</a>
|
| 60 |
+
"""
|
app/routers/auth/__init__.py
ADDED
|
File without changes
|
app/routers/auth/database.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sqlalchemy import create_engine
|
| 2 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 3 |
+
from sqlalchemy.orm import sessionmaker
|
| 4 |
+
|
| 5 |
+
# This creates a file named "sql_app.db" in your project root
|
| 6 |
+
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
|
| 7 |
+
|
| 8 |
+
# connect_args is needed only for SQLite
|
| 9 |
+
engine = create_engine(
|
| 10 |
+
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
| 11 |
+
)
|
| 12 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
| 13 |
+
|
| 14 |
+
Base = declarative_base()
|
| 15 |
+
|
| 16 |
+
# Dependency to get the DB session in endpoints
|
| 17 |
+
def get_db():
|
| 18 |
+
db = SessionLocal()
|
| 19 |
+
try:
|
| 20 |
+
yield db
|
| 21 |
+
finally:
|
| 22 |
+
db.close()
|
app/routers/auth/models.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sqlalchemy import Column, Integer, String, Boolean
|
| 2 |
+
from .database import Base
|
| 3 |
+
|
| 4 |
+
class User(Base):
|
| 5 |
+
__tablename__ = "users"
|
| 6 |
+
|
| 7 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 8 |
+
username = Column(String, unique=True, index=True)
|
| 9 |
+
email = Column(String, unique=True, index=True)
|
| 10 |
+
hashed_password = Column(String)
|
| 11 |
+
is_active = Column(Boolean, default=True)
|
app/routers/auth/schemas.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
|
| 3 |
+
class UserCreate(BaseModel):
|
| 4 |
+
username: str
|
| 5 |
+
email: str
|
| 6 |
+
password: str
|
| 7 |
+
|
| 8 |
+
class UserOut(BaseModel):
|
| 9 |
+
username: str
|
| 10 |
+
email: str
|
| 11 |
+
is_active: bool
|
| 12 |
+
|
| 13 |
+
class Config:
|
| 14 |
+
from_attributes = True
|
app/routers/auth/system.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, Depends, HTTPException, status
|
| 2 |
+
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
| 3 |
+
from sqlalchemy.orm import Session
|
| 4 |
+
from jose import JWTError, jwt
|
| 5 |
+
|
| 6 |
+
# Import local modules
|
| 7 |
+
from . import models, schemas, database
|
| 8 |
+
from .utils import verify_password, create_access_token, get_password_hash, SECRET_KEY, ALGORITHM
|
| 9 |
+
|
| 10 |
+
# Create tables automatically
|
| 11 |
+
models.Base.metadata.create_all(bind=database.engine)
|
| 12 |
+
|
| 13 |
+
router = APIRouter(
|
| 14 |
+
prefix="/auth",
|
| 15 |
+
tags=["Authentication"],
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
# Define the OAuth2 scheme
|
| 19 |
+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
|
| 20 |
+
|
| 21 |
+
# ==========================================
|
| 22 |
+
# 🔐 The Missing Dependency (Add this back!)
|
| 23 |
+
# ==========================================
|
| 24 |
+
async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(database.get_db)):
|
| 25 |
+
credentials_exception = HTTPException(
|
| 26 |
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 27 |
+
detail="Could not validate credentials",
|
| 28 |
+
headers={"WWW-Authenticate": "Bearer"},
|
| 29 |
+
)
|
| 30 |
+
try:
|
| 31 |
+
# Decode the token
|
| 32 |
+
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
| 33 |
+
username: str = payload.get("sub")
|
| 34 |
+
if username is None:
|
| 35 |
+
raise credentials_exception
|
| 36 |
+
except JWTError:
|
| 37 |
+
raise credentials_exception
|
| 38 |
+
|
| 39 |
+
# Check if user exists in DB
|
| 40 |
+
user = db.query(models.User).filter(models.User.username == username).first()
|
| 41 |
+
if user is None:
|
| 42 |
+
raise credentials_exception
|
| 43 |
+
|
| 44 |
+
return user
|
| 45 |
+
|
| 46 |
+
# ==========================================
|
| 47 |
+
# 🚀 Endpoints
|
| 48 |
+
# ==========================================
|
| 49 |
+
|
| 50 |
+
@router.post("/register", response_model=schemas.UserOut)
|
| 51 |
+
def register_user(user: schemas.UserCreate, db: Session = Depends(database.get_db)):
|
| 52 |
+
# 1. Check if user already exists
|
| 53 |
+
db_user = db.query(models.User).filter(models.User.username == user.username).first()
|
| 54 |
+
if db_user:
|
| 55 |
+
raise HTTPException(status_code=400, detail="Username already registered")
|
| 56 |
+
|
| 57 |
+
# 2. Hash the password
|
| 58 |
+
hashed_pw = get_password_hash(user.password)
|
| 59 |
+
|
| 60 |
+
# 3. Save to DB
|
| 61 |
+
new_user = models.User(
|
| 62 |
+
username=user.username,
|
| 63 |
+
email=user.email,
|
| 64 |
+
hashed_password=hashed_pw
|
| 65 |
+
)
|
| 66 |
+
db.add(new_user)
|
| 67 |
+
db.commit()
|
| 68 |
+
db.refresh(new_user)
|
| 69 |
+
|
| 70 |
+
return new_user
|
| 71 |
+
|
| 72 |
+
@router.post("/token")
|
| 73 |
+
async def login_for_access_token(
|
| 74 |
+
form_data: OAuth2PasswordRequestForm = Depends(),
|
| 75 |
+
db: Session = Depends(database.get_db)
|
| 76 |
+
):
|
| 77 |
+
# 1. Fetch user from DB
|
| 78 |
+
user = db.query(models.User).filter(models.User.username == form_data.username).first()
|
| 79 |
+
|
| 80 |
+
# 2. Verify password
|
| 81 |
+
if not user or not verify_password(form_data.password, user.hashed_password):
|
| 82 |
+
raise HTTPException(
|
| 83 |
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 84 |
+
detail="Incorrect username or password",
|
| 85 |
+
headers={"WWW-Authenticate": "Bearer"},
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
# 3. Create Token
|
| 89 |
+
access_token = create_access_token(data={"sub": user.username})
|
| 90 |
+
return {"access_token": access_token, "token_type": "bearer"}
|
app/routers/auth/utils.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datetime import datetime, timedelta
|
| 2 |
+
from typing import Union
|
| 3 |
+
from passlib.context import CryptContext
|
| 4 |
+
from jose import jwt
|
| 5 |
+
|
| 6 |
+
# ⚙️ CONFIGURATION (In production, load these from .env)
|
| 7 |
+
SECRET_KEY = "CHANGE_THIS_TO_A_REALLY_LONG_RANDOM_STRING"
|
| 8 |
+
ALGORITHM = "HS256"
|
| 9 |
+
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
| 10 |
+
|
| 11 |
+
# Password Hashing Wrapper
|
| 12 |
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
| 13 |
+
|
| 14 |
+
def verify_password(plain_password, hashed_password):
|
| 15 |
+
return pwd_context.verify(plain_password, hashed_password)
|
| 16 |
+
|
| 17 |
+
def get_password_hash(password):
|
| 18 |
+
return pwd_context.hash(password)
|
| 19 |
+
|
| 20 |
+
def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
|
| 21 |
+
to_encode = data.copy()
|
| 22 |
+
if expires_delta:
|
| 23 |
+
expire = datetime.utcnow() + expires_delta
|
| 24 |
+
else:
|
| 25 |
+
expire = datetime.utcnow() + timedelta(minutes=15)
|
| 26 |
+
|
| 27 |
+
to_encode.update({"exp": expire})
|
| 28 |
+
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
| 29 |
+
return encoded_jwt
|
app/routers/pdf/__init__.py
ADDED
|
File without changes
|
app/routers/pdf/pdf_tools.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, UploadFile, File, HTTPException, Security
|
| 2 |
+
from fastapi.responses import FileResponse
|
| 3 |
+
from pypdf import PdfReader, PdfWriter
|
| 4 |
+
import os
|
| 5 |
+
import shutil
|
| 6 |
+
import tempfile
|
| 7 |
+
|
| 8 |
+
# Define the APIRouter object
|
| 9 |
+
router = APIRouter(
|
| 10 |
+
prefix="/pdf",
|
| 11 |
+
tags=["PDF Manipulation"],
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
@router.post("/merge")
|
| 15 |
+
async def merge_pdfs(files: list[UploadFile] = File(description="List of PDFs to merge")):
|
| 16 |
+
"""
|
| 17 |
+
Merges multiple uploaded PDF files into a single PDF.
|
| 18 |
+
"""
|
| 19 |
+
if len(files) < 2:
|
| 20 |
+
raise HTTPException(status_code=400, detail="Please upload at least two PDF files to merge.")
|
| 21 |
+
|
| 22 |
+
pdf_writer = PdfWriter()
|
| 23 |
+
|
| 24 |
+
# Create a temporary directory to store files
|
| 25 |
+
with tempfile.TemporaryDirectory() as temp_dir:
|
| 26 |
+
output_path = os.path.join(temp_dir, "merged_output.pdf")
|
| 27 |
+
|
| 28 |
+
for file in files:
|
| 29 |
+
# Save the uploaded file temporarily
|
| 30 |
+
temp_file_path = os.path.join(temp_dir, file.filename)
|
| 31 |
+
with open(temp_file_path, "wb") as buffer:
|
| 32 |
+
shutil.copyfileobj(file.file, buffer)
|
| 33 |
+
|
| 34 |
+
# Read and append pages
|
| 35 |
+
try:
|
| 36 |
+
reader = PdfReader(temp_file_path)
|
| 37 |
+
for page in reader.pages:
|
| 38 |
+
pdf_writer.add_page(page)
|
| 39 |
+
except Exception as e:
|
| 40 |
+
raise HTTPException(status_code=500, detail=f"Error reading PDF file {file.filename}: {e}")
|
| 41 |
+
|
| 42 |
+
# Write the merged PDF to the output path
|
| 43 |
+
with open(output_path, "wb") as output_file:
|
| 44 |
+
pdf_writer.write(output_file)
|
| 45 |
+
|
| 46 |
+
# Return the merged file as a response
|
| 47 |
+
return FileResponse(output_path, media_type="application/pdf", filename="merged_document.pdf")
|
| 48 |
+
|
| 49 |
+
@router.post("/rotate")
|
| 50 |
+
async def rotate_pdf(
|
| 51 |
+
file: UploadFile = File(description="PDF file to rotate"),
|
| 52 |
+
degrees: int = 90 # Default rotation is 90 degrees clockwise
|
| 53 |
+
):
|
| 54 |
+
"""
|
| 55 |
+
Rotates all pages of a single PDF by a specified degree (90, 180, or 270).
|
| 56 |
+
"""
|
| 57 |
+
if degrees not in [90, 180, 270]:
|
| 58 |
+
raise HTTPException(status_code=400, detail="Rotation must be 90, 180, or 270 degrees.")
|
| 59 |
+
|
| 60 |
+
pdf_writer = PdfWriter()
|
| 61 |
+
|
| 62 |
+
with tempfile.TemporaryDirectory() as temp_dir:
|
| 63 |
+
input_path = os.path.join(temp_dir, file.filename)
|
| 64 |
+
output_path = os.path.join(temp_dir, "rotated_output.pdf")
|
| 65 |
+
|
| 66 |
+
# Save the uploaded file temporarily
|
| 67 |
+
with open(input_path, "wb") as buffer:
|
| 68 |
+
shutil.copyfileobj(file.file, buffer)
|
| 69 |
+
|
| 70 |
+
# Read, rotate, and write
|
| 71 |
+
try:
|
| 72 |
+
reader = PdfReader(input_path)
|
| 73 |
+
for page in reader.pages:
|
| 74 |
+
page.rotate(degrees)
|
| 75 |
+
pdf_writer.add_page(page)
|
| 76 |
+
except Exception as e:
|
| 77 |
+
raise HTTPException(status_code=500, detail=f"Error processing PDF: {e}")
|
| 78 |
+
|
| 79 |
+
# Write the rotated PDF
|
| 80 |
+
with open(output_path, "wb") as output_file:
|
| 81 |
+
pdf_writer.write(output_file)
|
| 82 |
+
|
| 83 |
+
return FileResponse(output_path, media_type="application/pdf", filename=f"rotated_{degrees}.pdf")
|
app/routers/security/__init__.py
ADDED
|
File without changes
|
app/routers/security/password_generator.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import string
|
| 3 |
+
from fastapi import APIRouter, Query, Depends # Added 'Depends'
|
| 4 |
+
from app.routers.auth.system import get_current_user # Import our Auth System
|
| 5 |
+
|
| 6 |
+
# Define the APIRouter object as per documentation standards
|
| 7 |
+
router = APIRouter(
|
| 8 |
+
prefix="/security", # Endpoints will start with /security
|
| 9 |
+
tags=["Password Tools"],
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
@router.get("/generate-password")
|
| 13 |
+
def generate_password(
|
| 14 |
+
length: int = Query(12, ge=8, le=64, description="Length of the password (8-64)"),
|
| 15 |
+
include_uppercase: bool = True,
|
| 16 |
+
include_numbers: bool = True,
|
| 17 |
+
include_symbols: bool = True,
|
| 18 |
+
# 👇 This line locks the endpoint!
|
| 19 |
+
current_user: dict = Depends(get_current_user)
|
| 20 |
+
):
|
| 21 |
+
"""
|
| 22 |
+
Generates a secure, random password.
|
| 23 |
+
**Requires Authentication.**
|
| 24 |
+
"""
|
| 25 |
+
# Base character set
|
| 26 |
+
chars = string.ascii_lowercase
|
| 27 |
+
|
| 28 |
+
# Advanced logic to append character sets based on flags
|
| 29 |
+
if include_uppercase:
|
| 30 |
+
chars += string.ascii_uppercase
|
| 31 |
+
if include_numbers:
|
| 32 |
+
chars += string.digits
|
| 33 |
+
if include_symbols:
|
| 34 |
+
chars += string.punctuation
|
| 35 |
+
|
| 36 |
+
# Generate the password
|
| 37 |
+
generated_password = "".join(random.choice(chars) for _ in range(length))
|
| 38 |
+
|
| 39 |
+
return {
|
| 40 |
+
"requested_by": current_user.username, # We can now see who asked for it!
|
| 41 |
+
"password": generated_password,
|
| 42 |
+
"length": length,
|
| 43 |
+
"complexity": "Advanced"
|
| 44 |
+
}
|
app/routers/testers/help.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
-
from fastapi import APIRouter
|
|
|
|
| 2 |
|
| 3 |
# Create the router for help/info endpoints
|
| 4 |
router = APIRouter(
|
|
@@ -7,13 +8,15 @@ router = APIRouter(
|
|
| 7 |
)
|
| 8 |
|
| 9 |
@router.get("/")
|
| 10 |
-
def get_api_info():
|
| 11 |
"""
|
| 12 |
Returns general information about the API and available endpoints.
|
| 13 |
"""
|
| 14 |
return {
|
|
|
|
| 15 |
"api_name": "My Awesome FastAPI",
|
| 16 |
"version": "1.0",
|
| 17 |
"available_sections": ["/random", "/help"],
|
| 18 |
"message": "Use the /docs endpoint for interactive API documentation."
|
|
|
|
| 19 |
}
|
|
|
|
| 1 |
+
from fastapi import APIRouter , Depends
|
| 2 |
+
from app.routers.auth.system import get_current_user # Import our Auth System
|
| 3 |
|
| 4 |
# Create the router for help/info endpoints
|
| 5 |
router = APIRouter(
|
|
|
|
| 8 |
)
|
| 9 |
|
| 10 |
@router.get("/")
|
| 11 |
+
def get_api_info(current_user: dict = Depends(get_current_user) ):
|
| 12 |
"""
|
| 13 |
Returns general information about the API and available endpoints.
|
| 14 |
"""
|
| 15 |
return {
|
| 16 |
+
"requested_by": current_user.username,
|
| 17 |
"api_name": "My Awesome FastAPI",
|
| 18 |
"version": "1.0",
|
| 19 |
"available_sections": ["/random", "/help"],
|
| 20 |
"message": "Use the /docs endpoint for interactive API documentation."
|
| 21 |
+
|
| 22 |
}
|
app/routers/testers/server_status.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, status
|
| 2 |
+
from fastapi.responses import JSONResponse
|
| 3 |
+
import platform
|
| 4 |
+
import psutil
|
| 5 |
+
|
| 6 |
+
# Initialize the router for this feature
|
| 7 |
+
router = APIRouter(
|
| 8 |
+
prefix="/server-status",
|
| 9 |
+
tags=["Server Info"],
|
| 10 |
+
responses={404: {"description": "Not found"}},
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
@router.get(
|
| 14 |
+
"/full-info",
|
| 15 |
+
summary="Get complete server/PC status and information",
|
| 16 |
+
status_code=status.HTTP_200_OK
|
| 17 |
+
)
|
| 18 |
+
def get_server_status():
|
| 19 |
+
"""
|
| 20 |
+
Retrieves detailed hardware and software status of the server.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
# --- System Information ---
|
| 24 |
+
system_info = {
|
| 25 |
+
"system": platform.system(),
|
| 26 |
+
"node_name": platform.node(),
|
| 27 |
+
"release": platform.release(),
|
| 28 |
+
"version": platform.version(),
|
| 29 |
+
"machine": platform.machine(),
|
| 30 |
+
"processor": platform.processor(),
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
# --- CPU Information ---
|
| 34 |
+
cpu_info = {
|
| 35 |
+
"physical_cores": psutil.cpu_count(logical=False),
|
| 36 |
+
"total_cores": psutil.cpu_count(logical=True),
|
| 37 |
+
"cpu_usage_percent": psutil.cpu_percent(interval=1),
|
| 38 |
+
"cpu_frequency_mhz": f"{psutil.cpu_freq().current:.2f}",
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
# --- Memory Information (RAM) ---
|
| 42 |
+
virtual_memory = psutil.virtual_memory()
|
| 43 |
+
memory_info = {
|
| 44 |
+
"total_gb": f"{virtual_memory.total / (1024**3):.2f}",
|
| 45 |
+
"available_gb": f"{virtual_memory.available / (1024**3):.2f}",
|
| 46 |
+
"used_gb": f"{virtual_memory.used / (1024**3):.2f}",
|
| 47 |
+
"usage_percent": virtual_memory.percent,
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
# --- Disk Usage Information (Root partition) ---
|
| 51 |
+
disk_usage = psutil.disk_usage('/')
|
| 52 |
+
disk_info = {
|
| 53 |
+
"total_gb": f"{disk_usage.total / (1024**3):.2f}",
|
| 54 |
+
"used_gb": f"{disk_usage.used / (1024**3):.2f}",
|
| 55 |
+
"free_gb": f"{disk_usage.free / (1024**3):.2f}",
|
| 56 |
+
"usage_percent": disk_usage.percent,
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
# --- Network Information (Simple) ---
|
| 60 |
+
network_info = {
|
| 61 |
+
"bytes_sent_mb": f"{psutil.net_io_counters().bytes_sent / (1024**2):.2f}",
|
| 62 |
+
"bytes_recv_mb": f"{psutil.net_io_counters().bytes_recv / (1024**2):.2f}",
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
response_data = {
|
| 66 |
+
"system_information": system_info,
|
| 67 |
+
"cpu_status": cpu_info,
|
| 68 |
+
"memory_status": memory_info,
|
| 69 |
+
"disk_status": disk_info,
|
| 70 |
+
"network_io": network_info,
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
return JSONResponse(content=response_data, status_code=status.HTTP_200_OK)
|
requirements.txt
CHANGED
|
@@ -2,4 +2,7 @@ fastapi
|
|
| 2 |
uvicorn
|
| 3 |
requests
|
| 4 |
pillow
|
| 5 |
-
python-multipart
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
uvicorn
|
| 3 |
requests
|
| 4 |
pillow
|
| 5 |
+
python-multipart
|
| 6 |
+
pypdf
|
| 7 |
+
python-dotenv
|
| 8 |
+
pydantic-settings
|
sql_app.db
ADDED
|
Binary file (20.5 kB). View file
|
|
|