Spaces:
Sleeping
Sleeping
Feat
Browse files- Dockerfile +0 -21
- app/main.py +16 -16
- requirements.txt +3 -1
- tests/api.py +4 -157
Dockerfile
CHANGED
|
@@ -1,24 +1,3 @@
|
|
| 1 |
-
# # Use an official Python runtime as a parent image
|
| 2 |
-
# FROM python:3.12-slim
|
| 3 |
-
|
| 4 |
-
# WORKDIR /code
|
| 5 |
-
|
| 6 |
-
# COPY ./requirements.txt /code/requirements.txt
|
| 7 |
-
|
| 8 |
-
# RUN pip install --no-cache-dir --upgrade pip && \
|
| 9 |
-
# pip install --no-cache-dir -r requirements.txt
|
| 10 |
-
|
| 11 |
-
# # Copy application code
|
| 12 |
-
# COPY ./app /code/app
|
| 13 |
-
# # Copy static files and templates
|
| 14 |
-
# COPY ./static /code/static
|
| 15 |
-
# COPY ./templates /code/templates
|
| 16 |
-
|
| 17 |
-
# EXPOSE 7860
|
| 18 |
-
|
| 19 |
-
# # Command to run the FastAPI application
|
| 20 |
-
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
| 21 |
-
|
| 22 |
# Use an official Python runtime as a parent image
|
| 23 |
FROM python:3.12-slim as builder
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# Use an official Python runtime as a parent image
|
| 2 |
FROM python:3.12-slim as builder
|
| 3 |
|
app/main.py
CHANGED
|
@@ -6,10 +6,8 @@ from fastapi.responses import HTMLResponse
|
|
| 6 |
from fastapi.staticfiles import StaticFiles
|
| 7 |
|
| 8 |
from .database import connect_db, disconnect_db, database, users
|
| 9 |
-
from .api import router as api_router
|
| 10 |
|
| 11 |
-
from
|
| 12 |
-
from sqlalchemy.dialects import sqlite
|
| 13 |
|
| 14 |
# Configure logging
|
| 15 |
logging.basicConfig(level=logging.INFO)
|
|
@@ -22,19 +20,16 @@ async def lifespan(app: FastAPI):
|
|
| 22 |
logger.info("Application startup: DB Connected. Checking/Creating tables...")
|
| 23 |
if database.is_connected:
|
| 24 |
try:
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
else: logger.error(f"Table '{users.name}' verification FAILED!")
|
| 36 |
-
else:
|
| 37 |
-
logger.info(f"Table '{users.name}' already exists.")
|
| 38 |
except Exception as db_setup_err:
|
| 39 |
logger.exception(f"CRITICAL error during async DB table setup: {db_setup_err}")
|
| 40 |
else:
|
|
@@ -63,6 +58,11 @@ async def read_root(request: Request):
|
|
| 63 |
logger.error("templates/index.html not found!")
|
| 64 |
return HTMLResponse(content="<html><body><h1>Error: Frontend not found</h1></body></html>", status_code=500)
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
if __name__ == "__main__":
|
| 67 |
import uvicorn
|
| 68 |
uvicorn.run("app.main:app", host="0.0.0.0", port=7860, reload=True)
|
|
|
|
| 6 |
from fastapi.staticfiles import StaticFiles
|
| 7 |
|
| 8 |
from .database import connect_db, disconnect_db, database, users
|
|
|
|
| 9 |
|
| 10 |
+
from .api import router as api_router
|
|
|
|
| 11 |
|
| 12 |
# Configure logging
|
| 13 |
logging.basicConfig(level=logging.INFO)
|
|
|
|
| 20 |
logger.info("Application startup: DB Connected. Checking/Creating tables...")
|
| 21 |
if database.is_connected:
|
| 22 |
try:
|
| 23 |
+
# Use CREATE TABLE IF NOT EXISTS to avoid race conditions with multiple workers
|
| 24 |
+
create_table_query = f"""
|
| 25 |
+
CREATE TABLE IF NOT EXISTS {users.name} (
|
| 26 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 27 |
+
email VARCHAR UNIQUE NOT NULL,
|
| 28 |
+
hashed_password VARCHAR NOT NULL
|
| 29 |
+
)
|
| 30 |
+
"""
|
| 31 |
+
await database.execute(query=create_table_query)
|
| 32 |
+
logger.info(f"Table '{users.name}' exists or was created.")
|
|
|
|
|
|
|
|
|
|
| 33 |
except Exception as db_setup_err:
|
| 34 |
logger.exception(f"CRITICAL error during async DB table setup: {db_setup_err}")
|
| 35 |
else:
|
|
|
|
| 58 |
logger.error("templates/index.html not found!")
|
| 59 |
return HTMLResponse(content="<html><body><h1>Error: Frontend not found</h1></body></html>", status_code=500)
|
| 60 |
|
| 61 |
+
@app.get("/health", status_code=200)
|
| 62 |
+
async def health_check():
|
| 63 |
+
"""Health check endpoint for container health monitoring"""
|
| 64 |
+
return {"status": "ok"}
|
| 65 |
+
|
| 66 |
if __name__ == "__main__":
|
| 67 |
import uvicorn
|
| 68 |
uvicorn.run("app.main:app", host="0.0.0.0", port=7860, reload=True)
|
requirements.txt
CHANGED
|
@@ -10,4 +10,6 @@ python-multipart==0.0.9
|
|
| 10 |
itsdangerous==2.1.2
|
| 11 |
websockets>=11.0.3,<13.0
|
| 12 |
aiofiles==23.2.1
|
| 13 |
-
httpx==0.27.0
|
|
|
|
|
|
|
|
|
| 10 |
itsdangerous==2.1.2
|
| 11 |
websockets>=11.0.3,<13.0
|
| 12 |
aiofiles==23.2.1
|
| 13 |
+
httpx==0.27.0
|
| 14 |
+
aiohttp==3.9.3
|
| 15 |
+
Faker==22.2.0
|
tests/api.py
CHANGED
|
@@ -1,156 +1,3 @@
|
|
| 1 |
-
# import requests
|
| 2 |
-
# import time
|
| 3 |
-
# from faker import Faker
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
# class AuthClient:
|
| 7 |
-
# """
|
| 8 |
-
# Python client for interacting with the Authentication API
|
| 9 |
-
# """
|
| 10 |
-
|
| 11 |
-
# def __init__(self, base_url="http://localhost:7860/api"):
|
| 12 |
-
# """
|
| 13 |
-
# Initialize the client with the API base URL
|
| 14 |
-
|
| 15 |
-
# Args:
|
| 16 |
-
# base_url (str): The base URL of the API
|
| 17 |
-
# """
|
| 18 |
-
# self.base_url = base_url
|
| 19 |
-
# self.token = None
|
| 20 |
-
|
| 21 |
-
# def register(self, email, password):
|
| 22 |
-
# """
|
| 23 |
-
# Register a new user
|
| 24 |
-
|
| 25 |
-
# Args:
|
| 26 |
-
# email (str): User's email
|
| 27 |
-
# password (str): User's password (should be at least 8 characters)
|
| 28 |
-
|
| 29 |
-
# Returns:
|
| 30 |
-
# dict: The user data returned by the API
|
| 31 |
-
|
| 32 |
-
# Raises:
|
| 33 |
-
# Exception: If registration fails
|
| 34 |
-
# """
|
| 35 |
-
# url = f"{self.base_url}/register"
|
| 36 |
-
# data = {
|
| 37 |
-
# "email": email,
|
| 38 |
-
# "password": password
|
| 39 |
-
# }
|
| 40 |
-
|
| 41 |
-
# response = requests.post(url, json=data)
|
| 42 |
-
|
| 43 |
-
# if response.status_code == 201:
|
| 44 |
-
# return response.json()
|
| 45 |
-
# else:
|
| 46 |
-
# error_detail = response.json().get("detail", "Unknown error")
|
| 47 |
-
# raise Exception(f"Registration failed: {error_detail} (Status: {response.status_code})")
|
| 48 |
-
|
| 49 |
-
# def login(self, email, password):
|
| 50 |
-
# """
|
| 51 |
-
# Login to obtain an authentication token
|
| 52 |
-
|
| 53 |
-
# Args:
|
| 54 |
-
# email (str): User's email
|
| 55 |
-
# password (str): User's password
|
| 56 |
-
|
| 57 |
-
# Returns:
|
| 58 |
-
# dict: The token data returned by the API
|
| 59 |
-
|
| 60 |
-
# Raises:
|
| 61 |
-
# Exception: If login fails
|
| 62 |
-
# """
|
| 63 |
-
# url = f"{self.base_url}/login"
|
| 64 |
-
# data = {
|
| 65 |
-
# "email": email,
|
| 66 |
-
# "password": password
|
| 67 |
-
# }
|
| 68 |
-
|
| 69 |
-
# response = requests.post(url, json=data)
|
| 70 |
-
|
| 71 |
-
# if response.status_code == 200:
|
| 72 |
-
# token_data = response.json()
|
| 73 |
-
# self.token = token_data["access_token"]
|
| 74 |
-
# return token_data
|
| 75 |
-
# else:
|
| 76 |
-
# error_detail = response.json().get("detail", "Unknown error")
|
| 77 |
-
# raise Exception(f"Login failed: {error_detail} (Status: {response.status_code})")
|
| 78 |
-
|
| 79 |
-
# def get_current_user(self):
|
| 80 |
-
# """
|
| 81 |
-
# Get information about the current logged-in user
|
| 82 |
-
|
| 83 |
-
# Returns:
|
| 84 |
-
# dict: The user data returned by the API
|
| 85 |
-
|
| 86 |
-
# Raises:
|
| 87 |
-
# Exception: If not authenticated or request fails
|
| 88 |
-
# """
|
| 89 |
-
# if not self.token:
|
| 90 |
-
# raise Exception("Not authenticated. Please login first.")
|
| 91 |
-
|
| 92 |
-
# url = f"{self.base_url}/users/me"
|
| 93 |
-
# headers = {"Authorization": f"Bearer {self.token}"}
|
| 94 |
-
|
| 95 |
-
# response = requests.get(url, headers=headers)
|
| 96 |
-
|
| 97 |
-
# if response.status_code == 200:
|
| 98 |
-
# return response.json()
|
| 99 |
-
# else:
|
| 100 |
-
# error_detail = response.json().get("detail", "Unknown error")
|
| 101 |
-
# raise Exception(f"Failed to get user info: {error_detail} (Status: {response.status_code})")
|
| 102 |
-
|
| 103 |
-
# def logout(self):
|
| 104 |
-
# """Clear the authentication token"""
|
| 105 |
-
# self.token = None
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
# # Example usage
|
| 109 |
-
# def main():
|
| 110 |
-
# # Initialize the client
|
| 111 |
-
# client = AuthClient("https://amaye15-authenticationapp.hf.space/api")
|
| 112 |
-
|
| 113 |
-
# # Initialize Faker
|
| 114 |
-
# fake = Faker()
|
| 115 |
-
|
| 116 |
-
# for i in range(10):
|
| 117 |
-
# try:
|
| 118 |
-
# # Generate random user data
|
| 119 |
-
# first_name = fake.first_name()
|
| 120 |
-
# last_name = fake.last_name()
|
| 121 |
-
# email = fake.email()
|
| 122 |
-
# password = fake.password(length=12, special_chars=True, digits=True, upper_case=True, lower_case=True)
|
| 123 |
-
|
| 124 |
-
# # Register a new user
|
| 125 |
-
# print(f"Registering a new user: {first_name} {last_name}...")
|
| 126 |
-
# try:
|
| 127 |
-
# user = client.register(email, password)
|
| 128 |
-
# print(f"Registered user: {user}")
|
| 129 |
-
# except Exception as e:
|
| 130 |
-
# print(f"Registration failed: {e}")
|
| 131 |
-
|
| 132 |
-
# # Login
|
| 133 |
-
# print("\nLogging in...")
|
| 134 |
-
# token_data = client.login(email, password)
|
| 135 |
-
# print(f"Login successful, token: {token_data['access_token'][:10]}...")
|
| 136 |
-
|
| 137 |
-
# # Get current user
|
| 138 |
-
# print("\nGetting current user info...")
|
| 139 |
-
# user_info = client.get_current_user()
|
| 140 |
-
# print(f"Current user: {user_info}")
|
| 141 |
-
|
| 142 |
-
# # Logout
|
| 143 |
-
# print("\nLogging out...")
|
| 144 |
-
# client.logout()
|
| 145 |
-
# print("Logged out successfully")
|
| 146 |
-
|
| 147 |
-
# except Exception as e:
|
| 148 |
-
# print(f"Error: {e}")
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
# if __name__ == "__main__":
|
| 152 |
-
# main()
|
| 153 |
-
|
| 154 |
import asyncio
|
| 155 |
import aiohttp
|
| 156 |
import time
|
|
@@ -359,9 +206,9 @@ async def load_test(num_users=10, concurrency=5, base_url="https://amaye15-authe
|
|
| 359 |
|
| 360 |
|
| 361 |
# Example usage
|
| 362 |
-
async def
|
| 363 |
# Initialize the client
|
| 364 |
-
base_url = "
|
| 365 |
|
| 366 |
# Run a simple example with a single user
|
| 367 |
fake = Faker()
|
|
@@ -398,8 +245,8 @@ async def main():
|
|
| 398 |
|
| 399 |
# Run a load test
|
| 400 |
print("\nRunning load test...")
|
| 401 |
-
await load_test(
|
| 402 |
|
| 403 |
|
| 404 |
if __name__ == "__main__":
|
| 405 |
-
asyncio.run(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import aiohttp
|
| 3 |
import time
|
|
|
|
| 206 |
|
| 207 |
|
| 208 |
# Example usage
|
| 209 |
+
async def remote():
|
| 210 |
# Initialize the client
|
| 211 |
+
base_url = "http://localhost:7860/api"
|
| 212 |
|
| 213 |
# Run a simple example with a single user
|
| 214 |
fake = Faker()
|
|
|
|
| 245 |
|
| 246 |
# Run a load test
|
| 247 |
print("\nRunning load test...")
|
| 248 |
+
await load_test(100, 10, base_url)
|
| 249 |
|
| 250 |
|
| 251 |
if __name__ == "__main__":
|
| 252 |
+
asyncio.run(remote())
|