Spaces:
Running
Running
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())
|