much much better
Browse files- Dockerfile +7 -0
- Makefile +12 -0
- README.md +1 -2
- backend/pyproject.toml +1 -0
- backend/src/app.py +93 -5
- backend/src/constants.py +11 -1
- backend/src/database.py +31 -0
- backend/src/schemas.py +3 -9
- backend/uv.lock +310 -0
- frontend/README.md +4 -11
- frontend/package.json +3 -1
- frontend/pnpm-lock.yaml +0 -0
- frontend/src/App.tsx +86 -31
- frontend/src/assets/huggingface.svg +37 -0
- frontend/src/assets/react.svg +0 -1
- frontend/src/assets/vite.svg +0 -1
- frontend/src/components/BackendHealthCheck.tsx +77 -50
- frontend/src/components/Counter.tsx +148 -0
- frontend/src/components/OAuthButton.tsx +116 -0
- frontend/src/config/constants.ts +3 -3
- frontend/src/utils/apiFetch.ts +19 -0
Dockerfile
CHANGED
@@ -51,12 +51,19 @@ FROM python:3.12-slim
|
|
51 |
|
52 |
# Create non-root user
|
53 |
RUN useradd -m appuser
|
|
|
|
|
|
|
|
|
54 |
WORKDIR /app
|
55 |
|
56 |
# Copy installed packages and app
|
57 |
COPY --from=backend-builder /usr/local /usr/local
|
58 |
COPY --from=backend-builder /app /app
|
59 |
|
|
|
|
|
|
|
60 |
# Set frontend path for FastAPI
|
61 |
ENV FRONTEND_PATH=/app/frontend/dist
|
62 |
|
|
|
51 |
|
52 |
# Create non-root user
|
53 |
RUN useradd -m appuser
|
54 |
+
|
55 |
+
# Create tmp_data directory and set permissions
|
56 |
+
RUN mkdir -p /app/tmp_data && chown appuser:appuser /app/tmp_data
|
57 |
+
|
58 |
WORKDIR /app
|
59 |
|
60 |
# Copy installed packages and app
|
61 |
COPY --from=backend-builder /usr/local /usr/local
|
62 |
COPY --from=backend-builder /app /app
|
63 |
|
64 |
+
# Ensure tmp_data directory has correct ownership after copying
|
65 |
+
RUN chown -R appuser:appuser /app/tmp_data
|
66 |
+
|
67 |
# Set frontend path for FastAPI
|
68 |
ENV FRONTEND_PATH=/app/frontend/dist
|
69 |
|
Makefile
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.PHONY: style quality dev-backend dev-frontend
|
2 |
+
|
3 |
+
BACKEND_DIR := backend
|
4 |
+
FRONTEND_DIR := frontend
|
5 |
+
|
6 |
+
style:
|
7 |
+
cd $(BACKEND_DIR) && make style
|
8 |
+
cd $(FRONTEND_DIR) && pnpm style
|
9 |
+
|
10 |
+
quality:
|
11 |
+
cd $(BACKEND_DIR) && make quality
|
12 |
+
cd $(FRONTEND_DIR) && pnpm quality
|
README.md
CHANGED
@@ -8,8 +8,7 @@ app_port: 8000
|
|
8 |
pinned: false
|
9 |
license: mit
|
10 |
short_description: Template to vibe code a demo running on FastAPI + React
|
11 |
-
|
12 |
-
- docker-space-fastapi-react
|
13 |
---
|
14 |
|
15 |
# docker-space-fastapi-react
|
|
|
8 |
pinned: false
|
9 |
license: mit
|
10 |
short_description: Template to vibe code a demo running on FastAPI + React
|
11 |
+
hf_oauth: true
|
|
|
12 |
---
|
13 |
|
14 |
# docker-space-fastapi-react
|
backend/pyproject.toml
CHANGED
@@ -11,6 +11,7 @@ dependencies = [
|
|
11 |
"fastapi",
|
12 |
"uvicorn",
|
13 |
"sqlmodel",
|
|
|
14 |
]
|
15 |
|
16 |
[dependency-groups]
|
|
|
11 |
"fastapi",
|
12 |
"uvicorn",
|
13 |
"sqlmodel",
|
14 |
+
"huggingface-hub[oauth]>=0.34.4",
|
15 |
]
|
16 |
|
17 |
[dependency-groups]
|
backend/src/app.py
CHANGED
@@ -1,23 +1,44 @@
|
|
1 |
-
from
|
|
|
|
|
2 |
from fastapi.middleware.cors import CORSMiddleware
|
3 |
-
from fastapi.responses import FileResponse
|
4 |
from fastapi.staticfiles import StaticFiles
|
|
|
|
|
5 |
|
6 |
from . import constants
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
app = FastAPI()
|
9 |
|
10 |
# Lax on CORS headers (Spaces take care of security issues)
|
11 |
app.add_middleware(
|
12 |
CORSMiddleware,
|
13 |
-
allow_origins=[
|
|
|
|
|
|
|
|
|
14 |
allow_credentials=True,
|
15 |
allow_methods=["*"],
|
16 |
allow_headers=["*"],
|
17 |
)
|
18 |
|
19 |
-
# Mount
|
20 |
if constants.SERVE_FRONTEND:
|
|
|
21 |
app.mount(
|
22 |
"/assets",
|
23 |
StaticFiles(directory=constants.FRONTEND_ASSETS_PATH),
|
@@ -28,7 +49,74 @@ if constants.SERVE_FRONTEND:
|
|
28 |
async def serve_frontend():
|
29 |
return FileResponse(constants.FRONTEND_INDEX_PATH)
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
@app.get("/api/health")
|
33 |
async def health():
|
|
|
34 |
return {"status": "ok"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from contextlib import asynccontextmanager
|
2 |
+
|
3 |
+
from fastapi import Depends, FastAPI, HTTPException, Request
|
4 |
from fastapi.middleware.cors import CORSMiddleware
|
5 |
+
from fastapi.responses import FileResponse, RedirectResponse
|
6 |
from fastapi.staticfiles import StaticFiles
|
7 |
+
from huggingface_hub import OAuthInfo, attach_huggingface_oauth, parse_huggingface_oauth
|
8 |
+
from sqlmodel import select
|
9 |
|
10 |
from . import constants
|
11 |
+
from .database import get_session, init_db
|
12 |
+
from .schemas import UserCount
|
13 |
+
|
14 |
+
|
15 |
+
# Initialize database on startup
|
16 |
+
@asynccontextmanager
|
17 |
+
async def lifespan(app: FastAPI):
|
18 |
+
init_db()
|
19 |
+
yield
|
20 |
+
|
21 |
+
|
22 |
+
# FastAPI app
|
23 |
+
app = FastAPI(lifespan=lifespan)
|
24 |
|
|
|
25 |
|
26 |
# Lax on CORS headers (Spaces take care of security issues)
|
27 |
app.add_middleware(
|
28 |
CORSMiddleware,
|
29 |
+
allow_origins=[
|
30 |
+
"http://0.0.0.0:8000",
|
31 |
+
"http://localhost:5173",
|
32 |
+
"https://huggingface.co",
|
33 |
+
],
|
34 |
allow_credentials=True,
|
35 |
allow_methods=["*"],
|
36 |
allow_headers=["*"],
|
37 |
)
|
38 |
|
39 |
+
# Mount frontend from dist directory (if configured)
|
40 |
if constants.SERVE_FRONTEND:
|
41 |
+
# Production => Serve frontend from dist directory
|
42 |
app.mount(
|
43 |
"/assets",
|
44 |
StaticFiles(directory=constants.FRONTEND_ASSETS_PATH),
|
|
|
49 |
async def serve_frontend():
|
50 |
return FileResponse(constants.FRONTEND_INDEX_PATH)
|
51 |
|
52 |
+
else:
|
53 |
+
# Development => Redirect to dev frontend
|
54 |
+
@app.get("/")
|
55 |
+
async def redirect_to_frontend():
|
56 |
+
return RedirectResponse("http://localhost:5173/")
|
57 |
+
|
58 |
+
|
59 |
+
# Set up Hugging Face OAuth
|
60 |
+
# To get OAuthInfo in an endpoint
|
61 |
+
attach_huggingface_oauth(app)
|
62 |
+
|
63 |
+
|
64 |
+
async def oauth_info_optional(request: Request) -> OAuthInfo | None:
|
65 |
+
return parse_huggingface_oauth(request)
|
66 |
|
67 |
+
|
68 |
+
async def oauth_info_required(request: Request) -> OAuthInfo:
|
69 |
+
oauth_info = parse_huggingface_oauth(request)
|
70 |
+
if oauth_info is None:
|
71 |
+
raise HTTPException(
|
72 |
+
status_code=401, detail="Unauthorized. Please Sign in with Hugging Face."
|
73 |
+
)
|
74 |
+
return oauth_info
|
75 |
+
|
76 |
+
|
77 |
+
# Health check endpoint
|
78 |
@app.get("/api/health")
|
79 |
async def health():
|
80 |
+
"""Health check endpoint."""
|
81 |
return {"status": "ok"}
|
82 |
+
|
83 |
+
|
84 |
+
# User endpoints
|
85 |
+
@app.get("/api/user")
|
86 |
+
async def get_user(oauth_info: OAuthInfo | None = Depends(oauth_info_optional)):
|
87 |
+
"""Get user information."""
|
88 |
+
return {
|
89 |
+
"connected": oauth_info is not None,
|
90 |
+
"username": oauth_info.user_info.preferred_username if oauth_info else None,
|
91 |
+
}
|
92 |
+
|
93 |
+
|
94 |
+
@app.get("/api/user/count")
|
95 |
+
async def get_user_count(
|
96 |
+
oauth_info: OAuthInfo = Depends(oauth_info_required),
|
97 |
+
) -> UserCount:
|
98 |
+
"""Get user count."""
|
99 |
+
with get_session() as session:
|
100 |
+
statement = select(UserCount).where(UserCount.name == oauth_info.user_info.name)
|
101 |
+
user_count = session.exec(statement).first()
|
102 |
+
if user_count is None:
|
103 |
+
user_count = UserCount(name=oauth_info.user_info.name, count=0)
|
104 |
+
return user_count
|
105 |
+
|
106 |
+
|
107 |
+
@app.post("/api/user/count/increment")
|
108 |
+
async def increment_user_count(
|
109 |
+
oauth_info: OAuthInfo = Depends(oauth_info_required),
|
110 |
+
) -> UserCount:
|
111 |
+
"""Increment user count."""
|
112 |
+
with get_session() as session:
|
113 |
+
statement = select(UserCount).where(UserCount.name == oauth_info.user_info.name)
|
114 |
+
user_count = session.exec(statement).first()
|
115 |
+
if user_count is None:
|
116 |
+
user_count = UserCount(name=oauth_info.user_info.name, count=0)
|
117 |
+
|
118 |
+
user_count.count += 1
|
119 |
+
session.add(user_count)
|
120 |
+
session.commit()
|
121 |
+
session.refresh(user_count)
|
122 |
+
return user_count
|
backend/src/constants.py
CHANGED
@@ -1,8 +1,18 @@
|
|
1 |
import os
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
FRONTEND_PATH = os.getenv("FRONTEND_PATH")
|
4 |
SERVE_FRONTEND = os.getenv("FRONTEND_PATH") is not None
|
5 |
-
|
6 |
FRONTEND_ASSETS_PATH = os.path.join(FRONTEND_PATH, "assets") if SERVE_FRONTEND else None
|
7 |
FRONTEND_INDEX_PATH = (
|
8 |
os.path.join(FRONTEND_PATH, "index.html") if SERVE_FRONTEND else None
|
|
|
1 |
import os
|
2 |
|
3 |
+
# Is server running in a Hugging Face Space?
|
4 |
+
IS_SPACE = os.getenv("SYSTEM") == "spaces"
|
5 |
+
|
6 |
+
# Database path
|
7 |
+
DATABASE_PATH = os.getenv("DATABASE_DIR", "tmp_data")
|
8 |
+
os.makedirs(DATABASE_PATH, exist_ok=True)
|
9 |
+
DATABASE_FILE = os.path.join(DATABASE_PATH, "database.db")
|
10 |
+
DATABASE_URL = f"sqlite:///{DATABASE_FILE}"
|
11 |
+
|
12 |
+
# Is frontend served by the server?
|
13 |
+
# In practice: is it running in a production environment?
|
14 |
FRONTEND_PATH = os.getenv("FRONTEND_PATH")
|
15 |
SERVE_FRONTEND = os.getenv("FRONTEND_PATH") is not None
|
|
|
16 |
FRONTEND_ASSETS_PATH = os.path.join(FRONTEND_PATH, "assets") if SERVE_FRONTEND else None
|
17 |
FRONTEND_INDEX_PATH = (
|
18 |
os.path.join(FRONTEND_PATH, "index.html") if SERVE_FRONTEND else None
|
backend/src/database.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from contextlib import contextmanager
|
2 |
+
from typing import Generator
|
3 |
+
|
4 |
+
from sqlalchemy.engine import Engine
|
5 |
+
from sqlmodel import Session, SQLModel, create_engine
|
6 |
+
|
7 |
+
from . import constants
|
8 |
+
|
9 |
+
_ENGINE_SINGLETON: Engine | None = None
|
10 |
+
|
11 |
+
|
12 |
+
def get_engine() -> Engine:
|
13 |
+
"""Get the engine."""
|
14 |
+
global _ENGINE_SINGLETON
|
15 |
+
if _ENGINE_SINGLETON is None:
|
16 |
+
_ENGINE_SINGLETON = create_engine(constants.DATABASE_URL)
|
17 |
+
return _ENGINE_SINGLETON
|
18 |
+
|
19 |
+
|
20 |
+
@contextmanager
|
21 |
+
def get_session() -> Generator[Session, None, None]:
|
22 |
+
"""Get a session from the engine."""
|
23 |
+
engine = get_engine()
|
24 |
+
with Session(engine) as session:
|
25 |
+
yield session
|
26 |
+
|
27 |
+
|
28 |
+
def init_db() -> None:
|
29 |
+
"""Initialize the database."""
|
30 |
+
engine = get_engine()
|
31 |
+
SQLModel.metadata.create_all(engine)
|
backend/src/schemas.py
CHANGED
@@ -1,12 +1,6 @@
|
|
1 |
-
from datetime import datetime
|
2 |
-
from typing import Literal
|
3 |
-
|
4 |
from sqlmodel import Field, SQLModel
|
5 |
|
6 |
|
7 |
-
class
|
8 |
-
|
9 |
-
|
10 |
-
status: Literal["pending", "running", "completed", "failed"]
|
11 |
-
created_at: datetime = Field(default_factory=datetime.now)
|
12 |
-
updated_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
|
|
|
|
|
1 |
from sqlmodel import Field, SQLModel
|
2 |
|
3 |
|
4 |
+
class UserCount(SQLModel, table=True):
|
5 |
+
name: str = Field(primary_key=True)
|
6 |
+
count: int
|
|
|
|
|
|
backend/uv.lock
CHANGED
@@ -25,6 +25,102 @@ wheels = [
|
|
25 |
{ url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
|
26 |
]
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
[[package]]
|
29 |
name = "click"
|
30 |
version = "8.2.1"
|
@@ -46,12 +142,48 @@ wheels = [
|
|
46 |
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
47 |
]
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
[[package]]
|
50 |
name = "docker-space-fastapi-react"
|
51 |
version = "0.1.0"
|
52 |
source = { virtual = "." }
|
53 |
dependencies = [
|
54 |
{ name = "fastapi" },
|
|
|
55 |
{ name = "sqlmodel" },
|
56 |
{ name = "uvicorn" },
|
57 |
]
|
@@ -65,6 +197,7 @@ dev = [
|
|
65 |
[package.metadata]
|
66 |
requires-dist = [
|
67 |
{ name = "fastapi" },
|
|
|
68 |
{ name = "sqlmodel" },
|
69 |
{ name = "uvicorn" },
|
70 |
]
|
@@ -89,6 +222,24 @@ wheels = [
|
|
89 |
{ url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" },
|
90 |
]
|
91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
[[package]]
|
93 |
name = "greenlet"
|
94 |
version = "3.2.4"
|
@@ -131,6 +282,76 @@ wheels = [
|
|
131 |
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
132 |
]
|
133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
[[package]]
|
135 |
name = "idna"
|
136 |
version = "3.10"
|
@@ -140,6 +361,33 @@ wheels = [
|
|
140 |
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
141 |
]
|
142 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
[[package]]
|
144 |
name = "pydantic"
|
145 |
version = "2.11.7"
|
@@ -197,6 +445,47 @@ wheels = [
|
|
197 |
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
|
198 |
]
|
199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
[[package]]
|
201 |
name = "ruff"
|
202 |
version = "0.12.8"
|
@@ -286,6 +575,18 @@ wheels = [
|
|
286 |
{ url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" },
|
287 |
]
|
288 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
[[package]]
|
290 |
name = "ty"
|
291 |
version = "0.0.1a17"
|
@@ -332,6 +633,15 @@ wheels = [
|
|
332 |
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
|
333 |
]
|
334 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
335 |
[[package]]
|
336 |
name = "uvicorn"
|
337 |
version = "0.35.0"
|
|
|
25 |
{ url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" },
|
26 |
]
|
27 |
|
28 |
+
[[package]]
|
29 |
+
name = "authlib"
|
30 |
+
version = "1.6.1"
|
31 |
+
source = { registry = "https://pypi.org/simple" }
|
32 |
+
dependencies = [
|
33 |
+
{ name = "cryptography" },
|
34 |
+
]
|
35 |
+
sdist = { url = "https://files.pythonhosted.org/packages/8e/a1/d8d1c6f8bc922c0b87ae0d933a8ed57be1bef6970894ed79c2852a153cd3/authlib-1.6.1.tar.gz", hash = "sha256:4dffdbb1460ba6ec8c17981a4c67af7d8af131231b5a36a88a1e8c80c111cdfd", size = 159988, upload-time = "2025-07-20T07:38:42.834Z" }
|
36 |
+
wheels = [
|
37 |
+
{ url = "https://files.pythonhosted.org/packages/f9/58/cc6a08053f822f98f334d38a27687b69c6655fb05cd74a7a5e70a2aeed95/authlib-1.6.1-py2.py3-none-any.whl", hash = "sha256:e9d2031c34c6309373ab845afc24168fe9e93dc52d252631f52642f21f5ed06e", size = 239299, upload-time = "2025-07-20T07:38:39.259Z" },
|
38 |
+
]
|
39 |
+
|
40 |
+
[[package]]
|
41 |
+
name = "certifi"
|
42 |
+
version = "2025.8.3"
|
43 |
+
source = { registry = "https://pypi.org/simple" }
|
44 |
+
sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" }
|
45 |
+
wheels = [
|
46 |
+
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
|
47 |
+
]
|
48 |
+
|
49 |
+
[[package]]
|
50 |
+
name = "cffi"
|
51 |
+
version = "1.17.1"
|
52 |
+
source = { registry = "https://pypi.org/simple" }
|
53 |
+
dependencies = [
|
54 |
+
{ name = "pycparser" },
|
55 |
+
]
|
56 |
+
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" }
|
57 |
+
wheels = [
|
58 |
+
{ url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" },
|
59 |
+
{ url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" },
|
60 |
+
{ url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" },
|
61 |
+
{ url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" },
|
62 |
+
{ url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" },
|
63 |
+
{ url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" },
|
64 |
+
{ url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" },
|
65 |
+
{ url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" },
|
66 |
+
{ url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" },
|
67 |
+
{ url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" },
|
68 |
+
{ url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" },
|
69 |
+
{ url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" },
|
70 |
+
{ url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" },
|
71 |
+
{ url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" },
|
72 |
+
{ url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" },
|
73 |
+
{ url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" },
|
74 |
+
{ url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" },
|
75 |
+
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" },
|
76 |
+
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" },
|
77 |
+
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" },
|
78 |
+
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" },
|
79 |
+
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" },
|
80 |
+
]
|
81 |
+
|
82 |
+
[[package]]
|
83 |
+
name = "charset-normalizer"
|
84 |
+
version = "3.4.3"
|
85 |
+
source = { registry = "https://pypi.org/simple" }
|
86 |
+
sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
|
87 |
+
wheels = [
|
88 |
+
{ url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" },
|
89 |
+
{ url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" },
|
90 |
+
{ url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" },
|
91 |
+
{ url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" },
|
92 |
+
{ url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" },
|
93 |
+
{ url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" },
|
94 |
+
{ url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" },
|
95 |
+
{ url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" },
|
96 |
+
{ url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" },
|
97 |
+
{ url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" },
|
98 |
+
{ url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" },
|
99 |
+
{ url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
|
100 |
+
{ url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
|
101 |
+
{ url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
|
102 |
+
{ url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
|
103 |
+
{ url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
|
104 |
+
{ url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
|
105 |
+
{ url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
|
106 |
+
{ url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
|
107 |
+
{ url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
|
108 |
+
{ url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
|
109 |
+
{ url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
|
110 |
+
{ url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
|
111 |
+
{ url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
|
112 |
+
{ url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
|
113 |
+
{ url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
|
114 |
+
{ url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
|
115 |
+
{ url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
|
116 |
+
{ url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
|
117 |
+
{ url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
|
118 |
+
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
|
119 |
+
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
|
120 |
+
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
|
121 |
+
{ url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
|
122 |
+
]
|
123 |
+
|
124 |
[[package]]
|
125 |
name = "click"
|
126 |
version = "8.2.1"
|
|
|
142 |
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
143 |
]
|
144 |
|
145 |
+
[[package]]
|
146 |
+
name = "cryptography"
|
147 |
+
version = "45.0.6"
|
148 |
+
source = { registry = "https://pypi.org/simple" }
|
149 |
+
dependencies = [
|
150 |
+
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
151 |
+
]
|
152 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/0d/d13399c94234ee8f3df384819dc67e0c5ce215fb751d567a55a1f4b028c7/cryptography-45.0.6.tar.gz", hash = "sha256:5c966c732cf6e4a276ce83b6e4c729edda2df6929083a952cc7da973c539c719", size = 744949, upload-time = "2025-08-05T23:59:27.93Z" }
|
153 |
+
wheels = [
|
154 |
+
{ url = "https://files.pythonhosted.org/packages/8c/29/2793d178d0eda1ca4a09a7c4e09a5185e75738cc6d526433e8663b460ea6/cryptography-45.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:048e7ad9e08cf4c0ab07ff7f36cc3115924e22e2266e034450a890d9e312dd74", size = 7042702, upload-time = "2025-08-05T23:58:23.464Z" },
|
155 |
+
{ url = "https://files.pythonhosted.org/packages/b3/b6/cabd07410f222f32c8d55486c464f432808abaa1f12af9afcbe8f2f19030/cryptography-45.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44647c5d796f5fc042bbc6d61307d04bf29bccb74d188f18051b635f20a9c75f", size = 4206483, upload-time = "2025-08-05T23:58:27.132Z" },
|
156 |
+
{ url = "https://files.pythonhosted.org/packages/8b/9e/f9c7d36a38b1cfeb1cc74849aabe9bf817990f7603ff6eb485e0d70e0b27/cryptography-45.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e40b80ecf35ec265c452eea0ba94c9587ca763e739b8e559c128d23bff7ebbbf", size = 4429679, upload-time = "2025-08-05T23:58:29.152Z" },
|
157 |
+
{ url = "https://files.pythonhosted.org/packages/9c/2a/4434c17eb32ef30b254b9e8b9830cee4e516f08b47fdd291c5b1255b8101/cryptography-45.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:00e8724bdad672d75e6f069b27970883179bd472cd24a63f6e620ca7e41cc0c5", size = 4210553, upload-time = "2025-08-05T23:58:30.596Z" },
|
158 |
+
{ url = "https://files.pythonhosted.org/packages/ef/1d/09a5df8e0c4b7970f5d1f3aff1b640df6d4be28a64cae970d56c6cf1c772/cryptography-45.0.6-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a3085d1b319d35296176af31c90338eeb2ddac8104661df79f80e1d9787b8b2", size = 3894499, upload-time = "2025-08-05T23:58:32.03Z" },
|
159 |
+
{ url = "https://files.pythonhosted.org/packages/79/62/120842ab20d9150a9d3a6bdc07fe2870384e82f5266d41c53b08a3a96b34/cryptography-45.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1b7fa6a1c1188c7ee32e47590d16a5a0646270921f8020efc9a511648e1b2e08", size = 4458484, upload-time = "2025-08-05T23:58:33.526Z" },
|
160 |
+
{ url = "https://files.pythonhosted.org/packages/fd/80/1bc3634d45ddfed0871bfba52cf8f1ad724761662a0c792b97a951fb1b30/cryptography-45.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:275ba5cc0d9e320cd70f8e7b96d9e59903c815ca579ab96c1e37278d231fc402", size = 4210281, upload-time = "2025-08-05T23:58:35.445Z" },
|
161 |
+
{ url = "https://files.pythonhosted.org/packages/7d/fe/ffb12c2d83d0ee625f124880a1f023b5878f79da92e64c37962bbbe35f3f/cryptography-45.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42", size = 4456890, upload-time = "2025-08-05T23:58:36.923Z" },
|
162 |
+
{ url = "https://files.pythonhosted.org/packages/8c/8e/b3f3fe0dc82c77a0deb5f493b23311e09193f2268b77196ec0f7a36e3f3e/cryptography-45.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ee411a1b977f40bd075392c80c10b58025ee5c6b47a822a33c1198598a7a5f05", size = 4333247, upload-time = "2025-08-05T23:58:38.781Z" },
|
163 |
+
{ url = "https://files.pythonhosted.org/packages/b3/a6/c3ef2ab9e334da27a1d7b56af4a2417d77e7806b2e0f90d6267ce120d2e4/cryptography-45.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e2a21a8eda2d86bb604934b6b37691585bd095c1f788530c1fcefc53a82b3453", size = 4565045, upload-time = "2025-08-05T23:58:40.415Z" },
|
164 |
+
{ url = "https://files.pythonhosted.org/packages/31/c3/77722446b13fa71dddd820a5faab4ce6db49e7e0bf8312ef4192a3f78e2f/cryptography-45.0.6-cp311-abi3-win32.whl", hash = "sha256:d063341378d7ee9c91f9d23b431a3502fc8bfacd54ef0a27baa72a0843b29159", size = 2928923, upload-time = "2025-08-05T23:58:41.919Z" },
|
165 |
+
{ url = "https://files.pythonhosted.org/packages/38/63/a025c3225188a811b82932a4dcc8457a26c3729d81578ccecbcce2cb784e/cryptography-45.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:833dc32dfc1e39b7376a87b9a6a4288a10aae234631268486558920029b086ec", size = 3403805, upload-time = "2025-08-05T23:58:43.792Z" },
|
166 |
+
{ url = "https://files.pythonhosted.org/packages/5b/af/bcfbea93a30809f126d51c074ee0fac5bd9d57d068edf56c2a73abedbea4/cryptography-45.0.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:3436128a60a5e5490603ab2adbabc8763613f638513ffa7d311c900a8349a2a0", size = 7020111, upload-time = "2025-08-05T23:58:45.316Z" },
|
167 |
+
{ url = "https://files.pythonhosted.org/packages/98/c6/ea5173689e014f1a8470899cd5beeb358e22bb3cf5a876060f9d1ca78af4/cryptography-45.0.6-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0d9ef57b6768d9fa58e92f4947cea96ade1233c0e236db22ba44748ffedca394", size = 4198169, upload-time = "2025-08-05T23:58:47.121Z" },
|
168 |
+
{ url = "https://files.pythonhosted.org/packages/ba/73/b12995edc0c7e2311ffb57ebd3b351f6b268fed37d93bfc6f9856e01c473/cryptography-45.0.6-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea3c42f2016a5bbf71825537c2ad753f2870191134933196bee408aac397b3d9", size = 4421273, upload-time = "2025-08-05T23:58:48.557Z" },
|
169 |
+
{ url = "https://files.pythonhosted.org/packages/f7/6e/286894f6f71926bc0da67408c853dd9ba953f662dcb70993a59fd499f111/cryptography-45.0.6-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:20ae4906a13716139d6d762ceb3e0e7e110f7955f3bc3876e3a07f5daadec5f3", size = 4199211, upload-time = "2025-08-05T23:58:50.139Z" },
|
170 |
+
{ url = "https://files.pythonhosted.org/packages/de/34/a7f55e39b9623c5cb571d77a6a90387fe557908ffc44f6872f26ca8ae270/cryptography-45.0.6-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dac5ec199038b8e131365e2324c03d20e97fe214af051d20c49db129844e8b3", size = 3883732, upload-time = "2025-08-05T23:58:52.253Z" },
|
171 |
+
{ url = "https://files.pythonhosted.org/packages/f9/b9/c6d32edbcba0cd9f5df90f29ed46a65c4631c4fbe11187feb9169c6ff506/cryptography-45.0.6-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:18f878a34b90d688982e43f4b700408b478102dd58b3e39de21b5ebf6509c301", size = 4450655, upload-time = "2025-08-05T23:58:53.848Z" },
|
172 |
+
{ url = "https://files.pythonhosted.org/packages/77/2d/09b097adfdee0227cfd4c699b3375a842080f065bab9014248933497c3f9/cryptography-45.0.6-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5bd6020c80c5b2b2242d6c48487d7b85700f5e0038e67b29d706f98440d66eb5", size = 4198956, upload-time = "2025-08-05T23:58:55.209Z" },
|
173 |
+
{ url = "https://files.pythonhosted.org/packages/55/66/061ec6689207d54effdff535bbdf85cc380d32dd5377173085812565cf38/cryptography-45.0.6-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:eccddbd986e43014263eda489abbddfbc287af5cddfd690477993dbb31e31016", size = 4449859, upload-time = "2025-08-05T23:58:56.639Z" },
|
174 |
+
{ url = "https://files.pythonhosted.org/packages/41/ff/e7d5a2ad2d035e5a2af116e1a3adb4d8fcd0be92a18032917a089c6e5028/cryptography-45.0.6-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:550ae02148206beb722cfe4ef0933f9352bab26b087af00e48fdfb9ade35c5b3", size = 4320254, upload-time = "2025-08-05T23:58:58.833Z" },
|
175 |
+
{ url = "https://files.pythonhosted.org/packages/82/27/092d311af22095d288f4db89fcaebadfb2f28944f3d790a4cf51fe5ddaeb/cryptography-45.0.6-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5b64e668fc3528e77efa51ca70fadcd6610e8ab231e3e06ae2bab3b31c2b8ed9", size = 4554815, upload-time = "2025-08-05T23:59:00.283Z" },
|
176 |
+
{ url = "https://files.pythonhosted.org/packages/7e/01/aa2f4940262d588a8fdf4edabe4cda45854d00ebc6eaac12568b3a491a16/cryptography-45.0.6-cp37-abi3-win32.whl", hash = "sha256:780c40fb751c7d2b0c6786ceee6b6f871e86e8718a8ff4bc35073ac353c7cd02", size = 2912147, upload-time = "2025-08-05T23:59:01.716Z" },
|
177 |
+
{ url = "https://files.pythonhosted.org/packages/0a/bc/16e0276078c2de3ceef6b5a34b965f4436215efac45313df90d55f0ba2d2/cryptography-45.0.6-cp37-abi3-win_amd64.whl", hash = "sha256:20d15aed3ee522faac1a39fbfdfee25d17b1284bafd808e1640a74846d7c4d1b", size = 3390459, upload-time = "2025-08-05T23:59:03.358Z" },
|
178 |
+
]
|
179 |
+
|
180 |
[[package]]
|
181 |
name = "docker-space-fastapi-react"
|
182 |
version = "0.1.0"
|
183 |
source = { virtual = "." }
|
184 |
dependencies = [
|
185 |
{ name = "fastapi" },
|
186 |
+
{ name = "huggingface-hub", extra = ["oauth"] },
|
187 |
{ name = "sqlmodel" },
|
188 |
{ name = "uvicorn" },
|
189 |
]
|
|
|
197 |
[package.metadata]
|
198 |
requires-dist = [
|
199 |
{ name = "fastapi" },
|
200 |
+
{ name = "huggingface-hub", extras = ["oauth"], specifier = ">=0.34.4" },
|
201 |
{ name = "sqlmodel" },
|
202 |
{ name = "uvicorn" },
|
203 |
]
|
|
|
222 |
{ url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" },
|
223 |
]
|
224 |
|
225 |
+
[[package]]
|
226 |
+
name = "filelock"
|
227 |
+
version = "3.18.0"
|
228 |
+
source = { registry = "https://pypi.org/simple" }
|
229 |
+
sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" }
|
230 |
+
wheels = [
|
231 |
+
{ url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" },
|
232 |
+
]
|
233 |
+
|
234 |
+
[[package]]
|
235 |
+
name = "fsspec"
|
236 |
+
version = "2025.7.0"
|
237 |
+
source = { registry = "https://pypi.org/simple" }
|
238 |
+
sdist = { url = "https://files.pythonhosted.org/packages/8b/02/0835e6ab9cfc03916fe3f78c0956cfcdb6ff2669ffa6651065d5ebf7fc98/fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58", size = 304432, upload-time = "2025-07-15T16:05:21.19Z" }
|
239 |
+
wheels = [
|
240 |
+
{ url = "https://files.pythonhosted.org/packages/2f/e0/014d5d9d7a4564cf1c40b5039bc882db69fd881111e03ab3657ac0b218e2/fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21", size = 199597, upload-time = "2025-07-15T16:05:19.529Z" },
|
241 |
+
]
|
242 |
+
|
243 |
[[package]]
|
244 |
name = "greenlet"
|
245 |
version = "3.2.4"
|
|
|
282 |
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
283 |
]
|
284 |
|
285 |
+
[[package]]
|
286 |
+
name = "hf-xet"
|
287 |
+
version = "1.1.7"
|
288 |
+
source = { registry = "https://pypi.org/simple" }
|
289 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b2/0a/a0f56735940fde6dd627602fec9ab3bad23f66a272397560abd65aba416e/hf_xet-1.1.7.tar.gz", hash = "sha256:20cec8db4561338824a3b5f8c19774055b04a8df7fff0cb1ff2cb1a0c1607b80", size = 477719, upload-time = "2025-08-06T00:30:55.741Z" }
|
290 |
+
wheels = [
|
291 |
+
{ url = "https://files.pythonhosted.org/packages/b1/7c/8d7803995caf14e7d19a392a486a040f923e2cfeff824e9b800b92072f76/hf_xet-1.1.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:60dae4b44d520819e54e216a2505685248ec0adbdb2dd4848b17aa85a0375cde", size = 2761743, upload-time = "2025-08-06T00:30:50.634Z" },
|
292 |
+
{ url = "https://files.pythonhosted.org/packages/51/a3/fa5897099454aa287022a34a30e68dbff0e617760f774f8bd1db17f06bd4/hf_xet-1.1.7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b109f4c11e01c057fc82004c9e51e6cdfe2cb230637644ade40c599739067b2e", size = 2624331, upload-time = "2025-08-06T00:30:49.212Z" },
|
293 |
+
{ url = "https://files.pythonhosted.org/packages/86/50/2446a132267e60b8a48b2e5835d6e24fd988000d0f5b9b15ebd6d64ef769/hf_xet-1.1.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efaaf1a5a9fc3a501d3e71e88a6bfebc69ee3a716d0e713a931c8b8d920038f", size = 3183844, upload-time = "2025-08-06T00:30:47.582Z" },
|
294 |
+
{ url = "https://files.pythonhosted.org/packages/20/8f/ccc670616bb9beee867c6bb7139f7eab2b1370fe426503c25f5cbb27b148/hf_xet-1.1.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:751571540f9c1fbad9afcf222a5fb96daf2384bf821317b8bfb0c59d86078513", size = 3074209, upload-time = "2025-08-06T00:30:45.509Z" },
|
295 |
+
{ url = "https://files.pythonhosted.org/packages/21/0a/4c30e1eb77205565b854f5e4a82cf1f056214e4dc87f2918ebf83d47ae14/hf_xet-1.1.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:18b61bbae92d56ae731b92087c44efcac216071182c603fc535f8e29ec4b09b8", size = 3239602, upload-time = "2025-08-06T00:30:52.41Z" },
|
296 |
+
{ url = "https://files.pythonhosted.org/packages/f5/1e/fc7e9baf14152662ef0b35fa52a6e889f770a7ed14ac239de3c829ecb47e/hf_xet-1.1.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:713f2bff61b252f8523739969f247aa354ad8e6d869b8281e174e2ea1bb8d604", size = 3348184, upload-time = "2025-08-06T00:30:54.105Z" },
|
297 |
+
{ url = "https://files.pythonhosted.org/packages/a3/73/e354eae84ceff117ec3560141224724794828927fcc013c5b449bf0b8745/hf_xet-1.1.7-cp37-abi3-win_amd64.whl", hash = "sha256:2e356da7d284479ae0f1dea3cf5a2f74fdf925d6dca84ac4341930d892c7cb34", size = 2820008, upload-time = "2025-08-06T00:30:57.056Z" },
|
298 |
+
]
|
299 |
+
|
300 |
+
[[package]]
|
301 |
+
name = "httpcore"
|
302 |
+
version = "1.0.9"
|
303 |
+
source = { registry = "https://pypi.org/simple" }
|
304 |
+
dependencies = [
|
305 |
+
{ name = "certifi" },
|
306 |
+
{ name = "h11" },
|
307 |
+
]
|
308 |
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
309 |
+
wheels = [
|
310 |
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
311 |
+
]
|
312 |
+
|
313 |
+
[[package]]
|
314 |
+
name = "httpx"
|
315 |
+
version = "0.28.1"
|
316 |
+
source = { registry = "https://pypi.org/simple" }
|
317 |
+
dependencies = [
|
318 |
+
{ name = "anyio" },
|
319 |
+
{ name = "certifi" },
|
320 |
+
{ name = "httpcore" },
|
321 |
+
{ name = "idna" },
|
322 |
+
]
|
323 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
324 |
+
wheels = [
|
325 |
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
326 |
+
]
|
327 |
+
|
328 |
+
[[package]]
|
329 |
+
name = "huggingface-hub"
|
330 |
+
version = "0.34.4"
|
331 |
+
source = { registry = "https://pypi.org/simple" }
|
332 |
+
dependencies = [
|
333 |
+
{ name = "filelock" },
|
334 |
+
{ name = "fsspec" },
|
335 |
+
{ name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
|
336 |
+
{ name = "packaging" },
|
337 |
+
{ name = "pyyaml" },
|
338 |
+
{ name = "requests" },
|
339 |
+
{ name = "tqdm" },
|
340 |
+
{ name = "typing-extensions" },
|
341 |
+
]
|
342 |
+
sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" }
|
343 |
+
wheels = [
|
344 |
+
{ url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" },
|
345 |
+
]
|
346 |
+
|
347 |
+
[package.optional-dependencies]
|
348 |
+
oauth = [
|
349 |
+
{ name = "authlib" },
|
350 |
+
{ name = "fastapi" },
|
351 |
+
{ name = "httpx" },
|
352 |
+
{ name = "itsdangerous" },
|
353 |
+
]
|
354 |
+
|
355 |
[[package]]
|
356 |
name = "idna"
|
357 |
version = "3.10"
|
|
|
361 |
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
362 |
]
|
363 |
|
364 |
+
[[package]]
|
365 |
+
name = "itsdangerous"
|
366 |
+
version = "2.2.0"
|
367 |
+
source = { registry = "https://pypi.org/simple" }
|
368 |
+
sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" }
|
369 |
+
wheels = [
|
370 |
+
{ url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" },
|
371 |
+
]
|
372 |
+
|
373 |
+
[[package]]
|
374 |
+
name = "packaging"
|
375 |
+
version = "25.0"
|
376 |
+
source = { registry = "https://pypi.org/simple" }
|
377 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
378 |
+
wheels = [
|
379 |
+
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
380 |
+
]
|
381 |
+
|
382 |
+
[[package]]
|
383 |
+
name = "pycparser"
|
384 |
+
version = "2.22"
|
385 |
+
source = { registry = "https://pypi.org/simple" }
|
386 |
+
sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" }
|
387 |
+
wheels = [
|
388 |
+
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" },
|
389 |
+
]
|
390 |
+
|
391 |
[[package]]
|
392 |
name = "pydantic"
|
393 |
version = "2.11.7"
|
|
|
445 |
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
|
446 |
]
|
447 |
|
448 |
+
[[package]]
|
449 |
+
name = "pyyaml"
|
450 |
+
version = "6.0.2"
|
451 |
+
source = { registry = "https://pypi.org/simple" }
|
452 |
+
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
|
453 |
+
wheels = [
|
454 |
+
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" },
|
455 |
+
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" },
|
456 |
+
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" },
|
457 |
+
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" },
|
458 |
+
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" },
|
459 |
+
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" },
|
460 |
+
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" },
|
461 |
+
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
|
462 |
+
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
|
463 |
+
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
|
464 |
+
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
|
465 |
+
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
|
466 |
+
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
|
467 |
+
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
|
468 |
+
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
|
469 |
+
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
|
470 |
+
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
|
471 |
+
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
|
472 |
+
]
|
473 |
+
|
474 |
+
[[package]]
|
475 |
+
name = "requests"
|
476 |
+
version = "2.32.4"
|
477 |
+
source = { registry = "https://pypi.org/simple" }
|
478 |
+
dependencies = [
|
479 |
+
{ name = "certifi" },
|
480 |
+
{ name = "charset-normalizer" },
|
481 |
+
{ name = "idna" },
|
482 |
+
{ name = "urllib3" },
|
483 |
+
]
|
484 |
+
sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
|
485 |
+
wheels = [
|
486 |
+
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
|
487 |
+
]
|
488 |
+
|
489 |
[[package]]
|
490 |
name = "ruff"
|
491 |
version = "0.12.8"
|
|
|
575 |
{ url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" },
|
576 |
]
|
577 |
|
578 |
+
[[package]]
|
579 |
+
name = "tqdm"
|
580 |
+
version = "4.67.1"
|
581 |
+
source = { registry = "https://pypi.org/simple" }
|
582 |
+
dependencies = [
|
583 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
584 |
+
]
|
585 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
|
586 |
+
wheels = [
|
587 |
+
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
|
588 |
+
]
|
589 |
+
|
590 |
[[package]]
|
591 |
name = "ty"
|
592 |
version = "0.0.1a17"
|
|
|
633 |
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
|
634 |
]
|
635 |
|
636 |
+
[[package]]
|
637 |
+
name = "urllib3"
|
638 |
+
version = "2.5.0"
|
639 |
+
source = { registry = "https://pypi.org/simple" }
|
640 |
+
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
641 |
+
wheels = [
|
642 |
+
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
643 |
+
]
|
644 |
+
|
645 |
[[package]]
|
646 |
name = "uvicorn"
|
647 |
version = "0.35.0"
|
frontend/README.md
CHANGED
@@ -4,7 +4,6 @@ This template provides a minimal setup to get React working in Vite with HMR and
|
|
4 |
|
5 |
Tailwind CSS plugin has been configured for rapid UI development. You can start styling immediately using utility classes without writing custom CSS from scratch.
|
6 |
|
7 |
-
|
8 |
## Getting started
|
9 |
|
10 |
### Install dependencies
|
@@ -22,6 +21,7 @@ pnpm dev
|
|
22 |
```
|
23 |
|
24 |
Frontend website should be available on localhost:
|
|
|
25 |
```
|
26 |
VITE v7.1.2 ready in 429 ms
|
27 |
|
@@ -34,23 +34,16 @@ Hot reload is enabled, meaning frontend is reloaded on each file update.
|
|
34 |
|
35 |
### Code quality
|
36 |
|
37 |
-
You can run the
|
38 |
|
39 |
```bash
|
40 |
-
pnpm
|
41 |
```
|
42 |
|
43 |
and then test everything's fine with:
|
44 |
|
45 |
```bash
|
46 |
-
pnpm
|
47 |
-
```
|
48 |
-
|
49 |
-
And for code formatting, run:
|
50 |
-
|
51 |
-
```bash
|
52 |
-
pnpm format
|
53 |
-
pnpm format:check
|
54 |
```
|
55 |
|
56 |
### Build
|
|
|
4 |
|
5 |
Tailwind CSS plugin has been configured for rapid UI development. You can start styling immediately using utility classes without writing custom CSS from scratch.
|
6 |
|
|
|
7 |
## Getting started
|
8 |
|
9 |
### Install dependencies
|
|
|
21 |
```
|
22 |
|
23 |
Frontend website should be available on localhost:
|
24 |
+
|
25 |
```
|
26 |
VITE v7.1.2 ready in 429 ms
|
27 |
|
|
|
34 |
|
35 |
### Code quality
|
36 |
|
37 |
+
You can run the formatter with:
|
38 |
|
39 |
```bash
|
40 |
+
pnpm style
|
41 |
```
|
42 |
|
43 |
and then test everything's fine with:
|
44 |
|
45 |
```bash
|
46 |
+
pnpm quality
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
```
|
48 |
|
49 |
### Build
|
frontend/package.json
CHANGED
@@ -8,9 +8,11 @@
|
|
8 |
"dev": "vite",
|
9 |
"build": "tsc -b && vite build",
|
10 |
"lint": "eslint --quiet --fix --ext .cjs,.ts .",
|
11 |
-
"lint:check": "eslint --ext .cjs,.ts .",
|
12 |
"format": "prettier --write .",
|
13 |
"format:check": "prettier --check .",
|
|
|
|
|
14 |
"preview": "vite preview"
|
15 |
},
|
16 |
"dependencies": {
|
|
|
8 |
"dev": "vite",
|
9 |
"build": "tsc -b && vite build",
|
10 |
"lint": "eslint --quiet --fix --ext .cjs,.ts .",
|
11 |
+
"lint:check": "eslint --quiet --ext .cjs,.ts .",
|
12 |
"format": "prettier --write .",
|
13 |
"format:check": "prettier --check .",
|
14 |
+
"style": "pnpm lint && pnpm format",
|
15 |
+
"quality": "pnpm lint:check && pnpm format:check",
|
16 |
"preview": "vite preview"
|
17 |
},
|
18 |
"dependencies": {
|
frontend/pnpm-lock.yaml
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
frontend/src/App.tsx
CHANGED
@@ -1,41 +1,96 @@
|
|
1 |
-
import { useState } from "react";
|
2 |
-
import reactLogo from "./assets/react.svg";
|
3 |
-
import viteLogo from "./assets/vite.svg";
|
4 |
import { BackendHealthCheck } from "./components/BackendHealthCheck";
|
|
|
|
|
|
|
5 |
|
6 |
function App() {
|
7 |
-
const [count, setCount] = useState(0);
|
8 |
-
|
9 |
return (
|
10 |
-
<div className="min-h-screen bg-gray-900 text-white flex
|
11 |
-
<
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
<
|
17 |
-
|
18 |
-
</
|
19 |
-
<a href="https://react.dev" target="_blank" className="hover:scale-110 transition-transform duration-300">
|
20 |
-
<img src={reactLogo} className="h-24 w-24 p-6 hover:drop-shadow-[0_0_2em_#61dafbaa] transition-all duration-300 animate-spin" alt="React logo" />
|
21 |
-
</a>
|
22 |
-
</div>
|
23 |
-
<h1 className="text-5xl font-bold mb-8">Vite + React</h1>
|
24 |
-
<div className="bg-gray-800 rounded-lg p-8 mb-8">
|
25 |
-
<button
|
26 |
-
onClick={() => setCount((count) => count + 1)}
|
27 |
-
className="bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg border border-transparent hover:border-blue-400 transition-all duration-250 focus:outline-none focus:ring-4 focus:ring-blue-500/50 mb-4"
|
28 |
-
>
|
29 |
-
count is {count}
|
30 |
-
</button>
|
31 |
-
<p className="text-gray-300">
|
32 |
-
Edit <code className="bg-gray-700 px-2 py-1 rounded text-sm">src/App.tsx</code> and save to test HMR
|
33 |
-
</p>
|
34 |
</div>
|
35 |
-
<p className="text-gray-
|
36 |
-
|
|
|
37 |
</p>
|
38 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
</div>
|
40 |
);
|
41 |
}
|
|
|
|
|
|
|
|
|
1 |
import { BackendHealthCheck } from "./components/BackendHealthCheck";
|
2 |
+
import OAuthButton from "./components/OAuthButton";
|
3 |
+
import Counter from "./components/Counter";
|
4 |
+
import huggingfaceLogo from "./assets/huggingface.svg";
|
5 |
|
6 |
function App() {
|
|
|
|
|
7 |
return (
|
8 |
+
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 text-white flex flex-col">
|
9 |
+
<BackendHealthCheck />
|
10 |
+
|
11 |
+
<div className="text-center pt-8 pb-6">
|
12 |
+
<div className="flex items-center justify-center space-x-3 mb-4">
|
13 |
+
<img src={huggingfaceLogo} alt="Hugging Face" className="h-8 w-8" />
|
14 |
+
<h1 className="text-3xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
|
15 |
+
FastAPI + React Template
|
16 |
+
</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
</div>
|
18 |
+
<p className="text-gray-300 text-lg max-w-2xl mx-auto">
|
19 |
+
A complete full-stack template to start vide-coding your own Hugging
|
20 |
+
Face Space.
|
21 |
</p>
|
22 |
</div>
|
23 |
+
|
24 |
+
<div className="flex-1 max-w-6xl mx-auto px-8">
|
25 |
+
<div className="bg-gray-800/50 backdrop-blur-sm rounded-2xl p-10 mb-8 border border-gray-700/50 shadow-2xl">
|
26 |
+
<div className="text-center mb-6">
|
27 |
+
<h2 className="text-2xl font-semibold mb-2">Try the demo</h2>
|
28 |
+
<p className="text-gray-300">
|
29 |
+
Sign in with Hugging Face to interact with the counter
|
30 |
+
</p>
|
31 |
+
</div>
|
32 |
+
<div className="space-y-6 max-w-md mx-auto">
|
33 |
+
<OAuthButton />
|
34 |
+
<Counter />
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
39 |
+
<div className="bg-gray-800/30 backdrop-blur-sm rounded-xl p-6 border border-gray-700/30">
|
40 |
+
<div className="text-blue-400 text-2xl mb-3">🚀</div>
|
41 |
+
<h3 className="text-lg font-semibold mb-2">Built for HF Spaces</h3>
|
42 |
+
<p className="text-gray-300 text-sm">
|
43 |
+
Pre-configured for instant deployment on Hugging Face Spaces
|
44 |
+
</p>
|
45 |
+
</div>
|
46 |
+
|
47 |
+
<div className="bg-gray-800/30 backdrop-blur-sm rounded-xl p-6 border border-gray-700/30">
|
48 |
+
<div className="text-pink-400 text-2xl mb-3">✨</div>
|
49 |
+
<h3 className="text-lg font-semibold mb-2">Vibe-coding ready</h3>
|
50 |
+
<p className="text-gray-300 text-sm">
|
51 |
+
Optimized for AI-assisted development
|
52 |
+
</p>
|
53 |
+
</div>
|
54 |
+
|
55 |
+
<div className="bg-gray-800/30 backdrop-blur-sm rounded-xl p-6 border border-gray-700/30">
|
56 |
+
<div className="text-yellow-400 text-2xl mb-3">🤗</div>
|
57 |
+
<h3 className="text-lg font-semibold mb-2">
|
58 |
+
Sign in with Hugging Face
|
59 |
+
</h3>
|
60 |
+
<p className="text-gray-300 text-sm">
|
61 |
+
OAuth integration, no setup required
|
62 |
+
</p>
|
63 |
+
</div>
|
64 |
+
|
65 |
+
<div className="bg-gray-800/30 backdrop-blur-sm rounded-xl p-6 border border-gray-700/30">
|
66 |
+
<div className="text-green-400 text-2xl mb-3">⚡</div>
|
67 |
+
<h3 className="text-lg font-semibold mb-2">FastAPI Backend</h3>
|
68 |
+
<p className="text-gray-300 text-sm">Modern Python backend</p>
|
69 |
+
</div>
|
70 |
+
|
71 |
+
<div className="bg-gray-800/30 backdrop-blur-sm rounded-xl p-6 border border-gray-700/30">
|
72 |
+
<div className="text-purple-400 text-2xl mb-3">🎨</div>
|
73 |
+
<h3 className="text-lg font-semibold mb-2">React + Tailwind</h3>
|
74 |
+
<p className="text-gray-300 text-sm">
|
75 |
+
Beautiful and responsive frontend
|
76 |
+
</p>
|
77 |
+
</div>
|
78 |
+
|
79 |
+
<div className="bg-gray-800/30 backdrop-blur-sm rounded-xl p-6 border border-gray-700/30">
|
80 |
+
<div className="text-cyan-400 text-2xl mb-3">💾</div>
|
81 |
+
<h3 className="text-lg font-semibold mb-2">SQLite Database</h3>
|
82 |
+
<p className="text-gray-300 text-sm">
|
83 |
+
(semi-)persistent data storage
|
84 |
+
</p>
|
85 |
+
</div>
|
86 |
+
</div>
|
87 |
+
|
88 |
+
<div className="bg-gray-800/20 backdrop-blur-sm rounded-xl p-6 border border-gray-700/20 mb-8">
|
89 |
+
<h3 className="text-xl font-semibold mb-4 text-center">
|
90 |
+
Duplicate this Space, open the README, and make it yours.
|
91 |
+
</h3>
|
92 |
+
</div>
|
93 |
+
</div>
|
94 |
</div>
|
95 |
);
|
96 |
}
|
frontend/src/assets/huggingface.svg
ADDED
|
frontend/src/assets/react.svg
DELETED
frontend/src/assets/vite.svg
DELETED
frontend/src/components/BackendHealthCheck.tsx
CHANGED
@@ -2,19 +2,15 @@ import { useState, useEffect } from "react";
|
|
2 |
import { BACKEND_URL, APP_CONFIG } from "../config/constants";
|
3 |
|
4 |
interface BackendHealthCheckProps {
|
5 |
-
backendUrl?: string;
|
6 |
checkInterval?: number;
|
7 |
}
|
8 |
|
9 |
-
export function BackendHealthCheck({
|
10 |
-
checkInterval = APP_CONFIG.HEALTH_CHECK_INTERVAL
|
11 |
}: BackendHealthCheckProps) {
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
}
|
16 |
-
|
17 |
-
const [backendStatus, setBackendStatus] = useState<'loading' | 'online' | 'offline'>('loading');
|
18 |
const [showTooltip, setShowTooltip] = useState(false);
|
19 |
const [copied, setCopied] = useState(false);
|
20 |
const healthCheckUrl = `${BACKEND_URL}/api/health`;
|
@@ -25,27 +21,32 @@ export function BackendHealthCheck({
|
|
25 |
const response = await fetch(healthCheckUrl);
|
26 |
if (response.ok) {
|
27 |
const data = await response.json();
|
28 |
-
if (data.status ===
|
29 |
-
setBackendStatus(
|
30 |
} else {
|
31 |
-
setBackendStatus(
|
32 |
}
|
33 |
} else {
|
34 |
-
setBackendStatus(
|
35 |
}
|
36 |
-
} catch
|
37 |
-
setBackendStatus(
|
38 |
}
|
39 |
};
|
40 |
|
41 |
checkBackendHealth();
|
42 |
-
|
43 |
// Check health at specified interval
|
44 |
const interval = setInterval(checkBackendHealth, checkInterval);
|
45 |
-
|
46 |
return () => clearInterval(interval);
|
47 |
}, [healthCheckUrl, checkInterval]);
|
48 |
|
|
|
|
|
|
|
|
|
|
|
49 |
const handleCopyCode = async () => {
|
50 |
const codeText = `cd backend/\nmake install\nmake backend`;
|
51 |
try {
|
@@ -53,67 +54,81 @@ export function BackendHealthCheck({
|
|
53 |
setCopied(true);
|
54 |
setTimeout(() => setCopied(false), 2000);
|
55 |
} catch (err) {
|
56 |
-
console.error(
|
57 |
}
|
58 |
};
|
59 |
|
60 |
const getStatusColor = () => {
|
61 |
switch (backendStatus) {
|
62 |
-
case
|
63 |
-
return
|
64 |
-
case
|
65 |
-
return
|
66 |
-
case
|
67 |
-
return
|
68 |
default:
|
69 |
-
return
|
70 |
}
|
71 |
};
|
72 |
|
73 |
const getStatusText = () => {
|
74 |
switch (backendStatus) {
|
75 |
-
case
|
76 |
-
return
|
77 |
-
case
|
78 |
-
return
|
79 |
-
case
|
80 |
-
return
|
81 |
default:
|
82 |
-
return
|
83 |
}
|
84 |
};
|
85 |
|
86 |
return (
|
87 |
<div className="absolute top-4 right-4">
|
88 |
-
<div
|
89 |
className="relative flex items-center space-x-2 bg-gray-800 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-700 transition-colors"
|
90 |
-
onMouseEnter={() => backendStatus ===
|
91 |
onMouseLeave={() => setShowTooltip(false)}
|
92 |
>
|
93 |
-
<div
|
|
|
|
|
94 |
<span className="text-sm font-medium">{getStatusText()}</span>
|
95 |
-
|
96 |
{/* Info icon when backend is offline */}
|
97 |
-
{backendStatus ===
|
98 |
-
|
99 |
-
|
100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
)}
|
102 |
-
|
103 |
{/* Tooltip for offline backend */}
|
104 |
-
{showTooltip && backendStatus ===
|
105 |
-
<div
|
106 |
className="absolute top-full right-0 mt-2 w-fit bg-gray-900 text-white rounded-lg p-4 shadow-xl border border-gray-700 z-50"
|
107 |
onMouseEnter={() => setShowTooltip(true)}
|
108 |
onMouseLeave={() => setShowTooltip(false)}
|
109 |
>
|
110 |
<div className="flex items-start space-x-3">
|
111 |
<div className="flex-1 min-w-0">
|
112 |
-
<h3 className="text-sm font-semibold text-400 mb-3 text-left">
|
|
|
|
|
113 |
<div className="relative">
|
114 |
<code className="text-xs bg-gray-800 text-green-400 px-3 py-2 rounded font-mono block w-fit overflow-x-auto text-left pr-12">
|
115 |
-
cd backend
|
116 |
-
|
|
|
|
|
117 |
make backend
|
118 |
</code>
|
119 |
<button
|
@@ -122,11 +137,23 @@ export function BackendHealthCheck({
|
|
122 |
title="Copy to clipboard"
|
123 |
>
|
124 |
{copied ? (
|
125 |
-
<svg
|
126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
</svg>
|
128 |
) : (
|
129 |
-
<svg
|
|
|
|
|
|
|
|
|
130 |
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
|
131 |
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
|
132 |
</svg>
|
@@ -142,4 +169,4 @@ export function BackendHealthCheck({
|
|
142 |
</div>
|
143 |
</div>
|
144 |
);
|
145 |
-
}
|
|
|
2 |
import { BACKEND_URL, APP_CONFIG } from "../config/constants";
|
3 |
|
4 |
interface BackendHealthCheckProps {
|
|
|
5 |
checkInterval?: number;
|
6 |
}
|
7 |
|
8 |
+
export function BackendHealthCheck({
|
9 |
+
checkInterval = APP_CONFIG.HEALTH_CHECK_INTERVAL,
|
10 |
}: BackendHealthCheckProps) {
|
11 |
+
const [backendStatus, setBackendStatus] = useState<
|
12 |
+
"loading" | "online" | "offline"
|
13 |
+
>("loading");
|
|
|
|
|
|
|
14 |
const [showTooltip, setShowTooltip] = useState(false);
|
15 |
const [copied, setCopied] = useState(false);
|
16 |
const healthCheckUrl = `${BACKEND_URL}/api/health`;
|
|
|
21 |
const response = await fetch(healthCheckUrl);
|
22 |
if (response.ok) {
|
23 |
const data = await response.json();
|
24 |
+
if (data.status === "ok") {
|
25 |
+
setBackendStatus("online");
|
26 |
} else {
|
27 |
+
setBackendStatus("offline");
|
28 |
}
|
29 |
} else {
|
30 |
+
setBackendStatus("offline");
|
31 |
}
|
32 |
+
} catch {
|
33 |
+
setBackendStatus("offline");
|
34 |
}
|
35 |
};
|
36 |
|
37 |
checkBackendHealth();
|
38 |
+
|
39 |
// Check health at specified interval
|
40 |
const interval = setInterval(checkBackendHealth, checkInterval);
|
41 |
+
|
42 |
return () => clearInterval(interval);
|
43 |
}, [healthCheckUrl, checkInterval]);
|
44 |
|
45 |
+
// Only render in development mode
|
46 |
+
if (import.meta.env.PROD) {
|
47 |
+
return null;
|
48 |
+
}
|
49 |
+
|
50 |
const handleCopyCode = async () => {
|
51 |
const codeText = `cd backend/\nmake install\nmake backend`;
|
52 |
try {
|
|
|
54 |
setCopied(true);
|
55 |
setTimeout(() => setCopied(false), 2000);
|
56 |
} catch (err) {
|
57 |
+
console.error("Failed to copy text: ", err);
|
58 |
}
|
59 |
};
|
60 |
|
61 |
const getStatusColor = () => {
|
62 |
switch (backendStatus) {
|
63 |
+
case "online":
|
64 |
+
return "bg-green-500";
|
65 |
+
case "offline":
|
66 |
+
return "bg-red-500";
|
67 |
+
case "loading":
|
68 |
+
return "bg-yellow-500";
|
69 |
default:
|
70 |
+
return "bg-gray-500";
|
71 |
}
|
72 |
};
|
73 |
|
74 |
const getStatusText = () => {
|
75 |
switch (backendStatus) {
|
76 |
+
case "online":
|
77 |
+
return "Backend Online";
|
78 |
+
case "offline":
|
79 |
+
return "Backend Offline";
|
80 |
+
case "loading":
|
81 |
+
return "Checking Backend...";
|
82 |
default:
|
83 |
+
return "Unknown Status";
|
84 |
}
|
85 |
};
|
86 |
|
87 |
return (
|
88 |
<div className="absolute top-4 right-4">
|
89 |
+
<div
|
90 |
className="relative flex items-center space-x-2 bg-gray-800 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-700 transition-colors"
|
91 |
+
onMouseEnter={() => backendStatus === "offline" && setShowTooltip(true)}
|
92 |
onMouseLeave={() => setShowTooltip(false)}
|
93 |
>
|
94 |
+
<div
|
95 |
+
className={`w-3 h-3 rounded-full ${getStatusColor()} animate-pulse`}
|
96 |
+
></div>
|
97 |
<span className="text-sm font-medium">{getStatusText()}</span>
|
98 |
+
|
99 |
{/* Info icon when backend is offline */}
|
100 |
+
{backendStatus === "offline" && (
|
101 |
+
<svg
|
102 |
+
className="w-5 h-5 text-red-400"
|
103 |
+
fill="currentColor"
|
104 |
+
viewBox="0 0 20 20"
|
105 |
+
>
|
106 |
+
<path
|
107 |
+
fillRule="evenodd"
|
108 |
+
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
|
109 |
+
clipRule="evenodd"
|
110 |
+
/>
|
111 |
+
</svg>
|
112 |
)}
|
113 |
+
|
114 |
{/* Tooltip for offline backend */}
|
115 |
+
{showTooltip && backendStatus === "offline" && (
|
116 |
+
<div
|
117 |
className="absolute top-full right-0 mt-2 w-fit bg-gray-900 text-white rounded-lg p-4 shadow-xl border border-gray-700 z-50"
|
118 |
onMouseEnter={() => setShowTooltip(true)}
|
119 |
onMouseLeave={() => setShowTooltip(false)}
|
120 |
>
|
121 |
<div className="flex items-start space-x-3">
|
122 |
<div className="flex-1 min-w-0">
|
123 |
+
<h3 className="text-sm font-semibold text-400 mb-3 text-left">
|
124 |
+
Start the server with:
|
125 |
+
</h3>
|
126 |
<div className="relative">
|
127 |
<code className="text-xs bg-gray-800 text-green-400 px-3 py-2 rounded font-mono block w-fit overflow-x-auto text-left pr-12">
|
128 |
+
cd backend/
|
129 |
+
<br />
|
130 |
+
make install
|
131 |
+
<br />
|
132 |
make backend
|
133 |
</code>
|
134 |
<button
|
|
|
137 |
title="Copy to clipboard"
|
138 |
>
|
139 |
{copied ? (
|
140 |
+
<svg
|
141 |
+
className="w-4 h-4 text-green-400"
|
142 |
+
fill="currentColor"
|
143 |
+
viewBox="0 0 20 20"
|
144 |
+
>
|
145 |
+
<path
|
146 |
+
fillRule="evenodd"
|
147 |
+
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
148 |
+
clipRule="evenodd"
|
149 |
+
/>
|
150 |
</svg>
|
151 |
) : (
|
152 |
+
<svg
|
153 |
+
className="w-4 h-4"
|
154 |
+
fill="currentColor"
|
155 |
+
viewBox="0 0 20 20"
|
156 |
+
>
|
157 |
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
|
158 |
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
|
159 |
</svg>
|
|
|
169 |
</div>
|
170 |
</div>
|
171 |
);
|
172 |
+
}
|
frontend/src/components/Counter.tsx
ADDED
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from "react";
|
2 |
+
import { apiFetch } from "../utils/apiFetch";
|
3 |
+
|
4 |
+
interface UserInfo {
|
5 |
+
connected: boolean;
|
6 |
+
username: string | null;
|
7 |
+
}
|
8 |
+
|
9 |
+
interface CountResponse {
|
10 |
+
name: string;
|
11 |
+
count: number;
|
12 |
+
}
|
13 |
+
|
14 |
+
const Counter: React.FC = () => {
|
15 |
+
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
|
16 |
+
const [count, setCount] = useState<number>(0);
|
17 |
+
const [loading, setLoading] = useState(true);
|
18 |
+
const [error, setError] = useState<string | null>(null);
|
19 |
+
const [incrementing, setIncrementing] = useState(false);
|
20 |
+
|
21 |
+
useEffect(() => {
|
22 |
+
const fetchUserInfo = async () => {
|
23 |
+
try {
|
24 |
+
setLoading(true);
|
25 |
+
const response = await apiFetch("/api/user");
|
26 |
+
if (!response.ok) {
|
27 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
28 |
+
}
|
29 |
+
const data: UserInfo = await response.json();
|
30 |
+
setUserInfo(data);
|
31 |
+
setError(null);
|
32 |
+
|
33 |
+
// If user is logged in, fetch their count
|
34 |
+
if (data.connected) {
|
35 |
+
await fetchUserCount();
|
36 |
+
}
|
37 |
+
} catch (err) {
|
38 |
+
setError(
|
39 |
+
err instanceof Error ? err.message : "Failed to fetch user info",
|
40 |
+
);
|
41 |
+
setUserInfo(null);
|
42 |
+
} finally {
|
43 |
+
setLoading(false);
|
44 |
+
}
|
45 |
+
};
|
46 |
+
|
47 |
+
fetchUserInfo();
|
48 |
+
}, []);
|
49 |
+
|
50 |
+
const fetchUserCount = async () => {
|
51 |
+
try {
|
52 |
+
const response = await apiFetch("/api/user/count");
|
53 |
+
if (!response.ok) {
|
54 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
55 |
+
}
|
56 |
+
const data: CountResponse = await response.json();
|
57 |
+
setCount(data.count);
|
58 |
+
} catch (err) {
|
59 |
+
console.error("Failed to fetch user count:", err);
|
60 |
+
setCount(0);
|
61 |
+
}
|
62 |
+
};
|
63 |
+
|
64 |
+
const handleIncrement = async () => {
|
65 |
+
if (!userInfo?.connected) {
|
66 |
+
setError("You must be logged in to increment the counter");
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
+
try {
|
71 |
+
setIncrementing(true);
|
72 |
+
setError(null);
|
73 |
+
|
74 |
+
const response = await apiFetch("/api/user/count/increment", {
|
75 |
+
method: "POST",
|
76 |
+
});
|
77 |
+
|
78 |
+
if (!response.ok) {
|
79 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
80 |
+
}
|
81 |
+
|
82 |
+
const data: CountResponse = await response.json();
|
83 |
+
setCount(data.count);
|
84 |
+
} catch (err) {
|
85 |
+
setError(
|
86 |
+
err instanceof Error ? err.message : "Failed to increment counter",
|
87 |
+
);
|
88 |
+
} finally {
|
89 |
+
setIncrementing(false);
|
90 |
+
}
|
91 |
+
};
|
92 |
+
|
93 |
+
// Loading state
|
94 |
+
if (loading) {
|
95 |
+
return (
|
96 |
+
<div className="flex items-center justify-center">
|
97 |
+
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
|
98 |
+
<span className="ml-2 text-gray-400 text-base">Loading...</span>
|
99 |
+
</div>
|
100 |
+
);
|
101 |
+
}
|
102 |
+
|
103 |
+
// Not logged in state
|
104 |
+
if (!userInfo?.connected) {
|
105 |
+
return (
|
106 |
+
<div className="text-center">
|
107 |
+
<p className="text-gray-400 text-base mb-4">
|
108 |
+
Please log in to use the counter
|
109 |
+
</p>
|
110 |
+
</div>
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
return (
|
115 |
+
<div className="space-y-4">
|
116 |
+
{error && (
|
117 |
+
<div className="p-3 bg-red-900/20 border border-red-700 rounded-lg">
|
118 |
+
<p className="text-red-400 text-base">{error}</p>
|
119 |
+
</div>
|
120 |
+
)}
|
121 |
+
|
122 |
+
<button
|
123 |
+
onClick={handleIncrement}
|
124 |
+
disabled={incrementing}
|
125 |
+
className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 disabled:from-gray-600 disabled:to-gray-700 text-white font-semibold py-4 px-8 rounded-xl border border-transparent hover:border-blue-400 transition-all duration-300 focus:outline-none focus:ring-4 focus:ring-blue-500/50 shadow-lg hover:shadow-xl transform hover:-translate-y-1 disabled:transform-none disabled:hover:shadow-lg relative"
|
126 |
+
>
|
127 |
+
<div
|
128 |
+
className={`flex items-center justify-center space-x-2 transition-opacity duration-200 ${incrementing ? "opacity-0" : "opacity-100"}`}
|
129 |
+
>
|
130 |
+
Count is {count}
|
131 |
+
</div>
|
132 |
+
|
133 |
+
<div
|
134 |
+
className={`absolute inset-0 flex items-center justify-center space-x-2 transition-opacity duration-200 ${incrementing ? "opacity-100" : "opacity-0"}`}
|
135 |
+
>
|
136 |
+
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
|
137 |
+
<span>Incrementing...</span>
|
138 |
+
</div>
|
139 |
+
</button>
|
140 |
+
|
141 |
+
<p className="text-gray-400 text-sm">
|
142 |
+
Logged in as: {userInfo.username || "User"}
|
143 |
+
</p>
|
144 |
+
</div>
|
145 |
+
);
|
146 |
+
};
|
147 |
+
|
148 |
+
export default Counter;
|
frontend/src/components/OAuthButton.tsx
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from "react";
|
2 |
+
import { BACKEND_URL } from "../config/constants";
|
3 |
+
import { apiFetch } from "../utils/apiFetch";
|
4 |
+
|
5 |
+
interface UserInfo {
|
6 |
+
connected: boolean;
|
7 |
+
username: string | null;
|
8 |
+
}
|
9 |
+
|
10 |
+
const OAuthButton: React.FC = () => {
|
11 |
+
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
|
12 |
+
const [loading, setLoading] = useState(true);
|
13 |
+
const [error, setError] = useState<string | null>(null);
|
14 |
+
|
15 |
+
useEffect(() => {
|
16 |
+
const fetchUserInfo = async () => {
|
17 |
+
try {
|
18 |
+
setLoading(true);
|
19 |
+
const response = await apiFetch("/api/user");
|
20 |
+
if (!response.ok) {
|
21 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
22 |
+
}
|
23 |
+
const data: UserInfo = await response.json();
|
24 |
+
setUserInfo(data);
|
25 |
+
setError(null);
|
26 |
+
} catch (err) {
|
27 |
+
setError(
|
28 |
+
err instanceof Error ? err.message : "Failed to fetch user info",
|
29 |
+
);
|
30 |
+
setUserInfo(null);
|
31 |
+
} finally {
|
32 |
+
setLoading(false);
|
33 |
+
}
|
34 |
+
};
|
35 |
+
|
36 |
+
fetchUserInfo();
|
37 |
+
}, []);
|
38 |
+
|
39 |
+
const handleLogin = () => {
|
40 |
+
const uri = `${BACKEND_URL}/oauth/huggingface/login`;
|
41 |
+
|
42 |
+
// Enable scrolling in parent window
|
43 |
+
window.parent?.postMessage({ type: "SET_SCROLLING", enabled: true }, "*");
|
44 |
+
|
45 |
+
// Redirect after a short delay
|
46 |
+
setTimeout(() => {
|
47 |
+
window.location.assign(uri + window.location.search);
|
48 |
+
}, 500);
|
49 |
+
};
|
50 |
+
|
51 |
+
const handleLogout = () => {
|
52 |
+
const uri = `${BACKEND_URL}/oauth/huggingface/logout`;
|
53 |
+
window.location.assign(uri);
|
54 |
+
};
|
55 |
+
|
56 |
+
// Loading state
|
57 |
+
if (loading) {
|
58 |
+
return (
|
59 |
+
<div className="flex items-center justify-center">
|
60 |
+
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
|
61 |
+
<span className="ml-2 text-gray-400 dark:text-gray-500 text-base">
|
62 |
+
Loading...
|
63 |
+
</span>
|
64 |
+
</div>
|
65 |
+
);
|
66 |
+
}
|
67 |
+
|
68 |
+
// Error state
|
69 |
+
if (error) {
|
70 |
+
return (
|
71 |
+
<div className="p-3 bg-red-900/20 dark:bg-red-100/20 border border-red-700 dark:border-red-300 rounded-lg">
|
72 |
+
<p className="text-red-400 dark:text-red-600 text-base">
|
73 |
+
Error: {error}
|
74 |
+
</p>
|
75 |
+
</div>
|
76 |
+
);
|
77 |
+
}
|
78 |
+
|
79 |
+
// Logged out state
|
80 |
+
if (!userInfo?.connected) {
|
81 |
+
return (
|
82 |
+
<button
|
83 |
+
onClick={handleLogin}
|
84 |
+
className="flex items-center justify-center space-x-3 px-4 py-2 bg-white dark:bg-[#0B0F19] hover:bg-gray-100 dark:hover:bg-[#1A1F2E] text-[#2C3236] dark:text-[#D6DAE2] rounded-full transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500/50 w-full h-12"
|
85 |
+
aria-label="Sign in with Hugging Face"
|
86 |
+
>
|
87 |
+
<img
|
88 |
+
src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
|
89 |
+
alt="Hugging Face"
|
90 |
+
className="h-5 w-5"
|
91 |
+
/>
|
92 |
+
<span className="font-medium text-base">Sign in with Hugging Face</span>
|
93 |
+
</button>
|
94 |
+
);
|
95 |
+
}
|
96 |
+
|
97 |
+
// Logged in state
|
98 |
+
return (
|
99 |
+
<button
|
100 |
+
onClick={handleLogout}
|
101 |
+
className="flex items-center justify-center space-x-3 px-4 py-2 bg-white dark:bg-[#0B0F19] hover:bg-gray-100 dark:hover:bg-[#1A1F2E] text-[#2C3236] dark:text-[#D6DAE2] rounded-full transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500/50 w-full h-12"
|
102 |
+
aria-label="Logout"
|
103 |
+
>
|
104 |
+
<img
|
105 |
+
src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
|
106 |
+
alt="Hugging Face"
|
107 |
+
className="h-5 w-5"
|
108 |
+
/>
|
109 |
+
<span className="font-medium text-base">
|
110 |
+
Logout ({userInfo.username || "User"})
|
111 |
+
</span>
|
112 |
+
</button>
|
113 |
+
);
|
114 |
+
};
|
115 |
+
|
116 |
+
export default OAuthButton;
|
frontend/src/config/constants.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
// Environment-based configuration constants
|
2 |
-
export const BACKEND_URL: string =
|
3 |
-
|
4 |
|
5 |
// App configuration
|
6 |
export const APP_CONFIG = {
|
7 |
HEALTH_CHECK_INTERVAL: 10000, // 10 seconds
|
8 |
} as const;
|
9 |
|
10 |
-
export type AppConfig = typeof APP_CONFIG;
|
|
|
1 |
// Environment-based configuration constants
|
2 |
+
export const BACKEND_URL: string =
|
3 |
+
import.meta.env.VITE_BACKEND_URL || "http://127.0.0.1:8000";
|
4 |
|
5 |
// App configuration
|
6 |
export const APP_CONFIG = {
|
7 |
HEALTH_CHECK_INTERVAL: 10000, // 10 seconds
|
8 |
} as const;
|
9 |
|
10 |
+
export type AppConfig = typeof APP_CONFIG;
|
frontend/src/utils/apiFetch.ts
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { BACKEND_URL } from "../config/constants";
|
2 |
+
|
3 |
+
export async function apiFetch(
|
4 |
+
endpoint: string,
|
5 |
+
options: RequestInit = {},
|
6 |
+
): Promise<Response> {
|
7 |
+
const url = `${BACKEND_URL}${endpoint}`;
|
8 |
+
|
9 |
+
const defaultOptions: RequestInit = {
|
10 |
+
credentials: "include",
|
11 |
+
headers: {
|
12 |
+
"Content-Type": "application/json",
|
13 |
+
...options.headers,
|
14 |
+
},
|
15 |
+
...options,
|
16 |
+
};
|
17 |
+
|
18 |
+
return fetch(url, defaultOptions);
|
19 |
+
}
|