This commit is contained in:
parent
e3cd4fa080
commit
56ec151b70
|
@ -7,4 +7,6 @@ s3fs
|
|||
aiofiles
|
||||
duckdb
|
||||
pyjwt[crypto]
|
||||
python-multipart
|
||||
python-multipart
|
||||
authlib
|
||||
itsdangerous
|
|
@ -2,11 +2,16 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
|||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
base_url: str = "http://localhost:8000"
|
||||
anyscale_api_key: str = "Awesome API"
|
||||
gcs_access: str = "access"
|
||||
gcs_secret: str = "secret"
|
||||
gcs_bucketname: str = "bucket"
|
||||
auth: bool = True
|
||||
auth0_client_id: str = ""
|
||||
auth0_client_secret: str = ""
|
||||
auth0_domain: str = ""
|
||||
app_secret_key: str = ""
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env")
|
||||
|
||||
|
|
|
@ -1,16 +1,76 @@
|
|||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, status
|
||||
from fastapi.responses import RedirectResponse, HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
from starlette.requests import Request
|
||||
from authlib.integrations.starlette_client import OAuth, OAuthError
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
|
||||
|
||||
import hellocomputer
|
||||
|
||||
from .routers import analysis, files, sessions
|
||||
from .config import settings
|
||||
|
||||
static_path = Path(hellocomputer.__file__).parent / "static"
|
||||
|
||||
oauth = OAuth()
|
||||
oauth.register(
|
||||
"auth0",
|
||||
client_id=settings.auth0_client_id,
|
||||
client_secret=settings.auth0_client_secret,
|
||||
client_kwargs={"scope": "openid profile email", "verify": False},
|
||||
server_metadata_url=f"https://{settings.auth0_domain}/.well-known/openid-configuration",
|
||||
)
|
||||
app = FastAPI()
|
||||
app.add_middleware(SessionMiddleware, secret_key=settings.app_secret_key)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def homepage(request: Request):
|
||||
user = request.session.get("user")
|
||||
if user:
|
||||
print(json.dumps(user))
|
||||
return RedirectResponse("/app")
|
||||
|
||||
with open(static_path / "login.html") as f:
|
||||
return HTMLResponse(f.read())
|
||||
|
||||
|
||||
@app.route("/login")
|
||||
async def login(request: Request):
|
||||
return await oauth.auth0.authorize_redirect(
|
||||
request,
|
||||
redirect_uri="http://localhost:8000/callback",
|
||||
)
|
||||
|
||||
|
||||
@app.route("/callback", methods=["GET", "POST"])
|
||||
async def callback(request: Request):
|
||||
try:
|
||||
token = await oauth.auth0.authorize_access_token(request)
|
||||
except OAuthError as error:
|
||||
return HTMLResponse(f"<h1>{error.error}</h1>")
|
||||
user = token.get("userinfo")
|
||||
if user:
|
||||
request.session["user"] = dict(user)
|
||||
|
||||
return RedirectResponse(url="/app")
|
||||
|
||||
|
||||
@app.route("/logout")
|
||||
async def logout(request: Request):
|
||||
request.session.pop("user", None)
|
||||
return RedirectResponse(url="/")
|
||||
|
||||
|
||||
@app.route("/user")
|
||||
async def user(request: Request):
|
||||
user = request.session.get("user")
|
||||
return user
|
||||
|
||||
|
||||
class HealthCheck(BaseModel):
|
||||
|
@ -44,7 +104,7 @@ app.include_router(sessions.router)
|
|||
app.include_router(files.router)
|
||||
app.include_router(analysis.router)
|
||||
app.mount(
|
||||
"/",
|
||||
"/app",
|
||||
StaticFiles(directory=static_path, html=True),
|
||||
name="static",
|
||||
)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from typing import Annotated
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter
|
||||
from starlette.requests import Request
|
||||
from fastapi.responses import PlainTextResponse
|
||||
|
||||
from ..security import oauth2_scheme
|
||||
|
||||
# Scheme for the Authorization header
|
||||
|
||||
|
@ -12,7 +11,9 @@ router = APIRouter()
|
|||
|
||||
|
||||
@router.get("/new_session")
|
||||
async def get_new_session(token: Annotated[str, Depends(oauth2_scheme)]) -> str:
|
||||
async def get_new_session(request: Request) -> str:
|
||||
user = request.session.get("user")
|
||||
print(user)
|
||||
return str(uuid4())
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
|
@ -8,6 +8,7 @@
|
|||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -21,9 +22,17 @@
|
|||
Hello, computer!
|
||||
</a>
|
||||
</p>
|
||||
<a href="#" class="list-group-item list-group-item-action bg-light">How to</a>
|
||||
<a href="#" class="list-group-item list-group-item-action bg-light">File templates</a>
|
||||
<a href="#" class="list-group-item list-group-item-action bg-light">About</a>
|
||||
<a href="#" class="list-group-item list-group-item-action bg-light"><i
|
||||
class="bi bi-question-circle"></i> How to</a>
|
||||
<a href="#" class="list-group-item list-group-item-action bg-light"><i class="bi bi-file-ruled"></i>
|
||||
File templates</a>
|
||||
<a href="#" class="list-group-item list-group-item-action bg-light"><i class="bi bi-info-circle"></i>
|
||||
About</a>
|
||||
<a href="/config" class="list-group-item list-group-item-action bg-light"><i class="bi bi-toggles"></i>
|
||||
Config</a>
|
||||
<a href="/logout" class="list-group-item list-group-item-action bg-light"><i
|
||||
class="bi bi-box-arrow-right"></i>
|
||||
Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -105,7 +114,7 @@
|
|||
<div class="chat-messages">
|
||||
<!-- Messages will be appended here -->
|
||||
<div class="message bg-white p-2 mb-2 rounded">
|
||||
<img src="/img/assistant.webp" width="50px">
|
||||
<img src="/app/img/assistant.webp" width="50px">
|
||||
<div id="content">
|
||||
<div id="spinner" class="spinner"></div>
|
||||
<div id="result" class="hidden"></div>
|
||||
|
|
43
src/hellocomputer/static/login.html
Normal file
43
src/hellocomputer/static/login.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Page</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 50px 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 20px auto;
|
||||
}
|
||||
|
||||
.techie-font {
|
||||
font-family: 'Share Tech Mono', monospace;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="login-container text-center">
|
||||
<h1 class="h3 mb-3 fw-normal techie-font">Hola, computer!</h1>
|
||||
<img src="/app/img/assistant.webp" alt="Logo" class="logo img-fluid">
|
||||
<a href="/login"><button type="submit" class="btn btn-primary w-100">Login</button></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -37,16 +37,16 @@ async function fetchResponse(message, newMessage) {
|
|||
const data = await response.text();
|
||||
|
||||
// Hide spinner and display result
|
||||
newMessage.innerHTML = '<img src="/img/assistant.webp" width="50px"> <div><pre>' + data + '</pre></div>';
|
||||
newMessage.innerHTML = '<img src="/app/img/assistant.webp" width="50px"> <div><pre>' + data + '</pre></div>';
|
||||
} catch (error) {
|
||||
newMessage.innerHTML = '<img src="/img/assistant.webp" width="50px">' + 'Error: ' + error.message;
|
||||
newMessage.innerHTML = '<img src="/app/img/assistant.webp" width="50px">' + 'Error: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
function addAIMessage(messageContent) {
|
||||
const newMessage = document.createElement('div');
|
||||
newMessage.classList.add('message', 'bg-white', 'p-2', 'mb-2', 'rounded');
|
||||
newMessage.innerHTML = '<img src="/img/assistant.webp" width="50px"> <div id="spinner" class="spinner"></div>';
|
||||
newMessage.innerHTML = '<img src="/app/img/assistant.webp" width="50px"> <div id="spinner" class="spinner"></div>';
|
||||
chatMessages.prepend(newMessage); // Add new message at the top
|
||||
fetchResponse(messageContent, newMessage);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ function addAIMessage(messageContent) {
|
|||
function addAIManualMessage(m) {
|
||||
const newMessage = document.createElement('div');
|
||||
newMessage.classList.add('message', 'bg-white', 'p-2', 'mb-2', 'rounded');
|
||||
newMessage.innerHTML = '<img src="/img/assistant.webp" width="50px"> <div>' + m + '</div>';
|
||||
newMessage.innerHTML = '<img src="/app/img/assistant.webp" width="50px"> <div>' + m + '</div>';
|
||||
chatMessages.prepend(newMessage); // Add new message at the top
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue