Sito web completo in Python: frontend e backend con l'IA
Tradotto dall'originale in spagnolo. Leggi in spagnolo
Oggi puoi costruire siti web moderni usando solo Python, sia nel frontend sia nel backend, e integrare l’intelligenza artificiale per creare esperienze avanzate. Qui ti mostro come farlo in modo sicuro, usando come esempio un generatore di storie per bambini.
Live Demo: https://bedtimestory.zerogap.uk/
Architettura
- Frontend: NiceGUI per un’interfaccia web moderna, senza JavaScript.
- Backend: FastAPI per la logica.
- IA: Azure OpenAI (tramite LangChain) per generare storie o qualsiasi altro compito di linguaggio naturale.
Esempio: generatore di storie con l’IA
Il codice seguente mostra come integrare frontend e backend e come proteggere i tuoi endpoint con la validazione.
Backend (FastAPI + Azure OpenAI tramite LangChain)
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel, validator
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from langchain_community.chat_models import AzureChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
import os
import re
from dotenv import load_dotenv
app = FastAPI()
def whitelist(text, max_length=50, pattern=r"[^a-zA-Z0-9áéíóúÁÉÍÓÚüÜñÑ\s.,;:!?'\"()-]"):
"""Allow only letters, numbers, punctuation and spaces up to max_length."""
if text is None:
return None
# Remove any disallowed character
clean = re.sub(pattern, "", text)
# Truncate and strip
return clean.strip()[:max_length]
class StoryRequest(BaseModel):
theme: str = None
main_character: str = None
@validator('theme', 'main_character')
def validate_field(cls, v):
if v is not None:
# Only allow reasonable length and allowed characters
v = whitelist(v)
if len(v) == 0:
raise ValueError("Field cannot be empty after sanitization.")
return v
@app.post("/generate")
async def generate_story(request: StoryRequest, fastapi_request: Request):
# Only allow local requests for security
client_host = fastapi_request.client.host
if client_host not in ("127.0.0.1", "::1", "localhost"):
raise HTTPException(status_code=403, detail="Forbidden")
load_dotenv()
endpoint = "https://your-private-resource.openai.azure.com/"
deployment = "my-private-deployment"
api_version = "2024-12-01-preview"
credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default")
prompt = f"Write a children's story about {request.theme or 'friendship'}, starring a {request.main_character or 'dragon'}."
system_prompt = "You are a creative children's storyteller. Only return the story."
messages = [
SystemMessage(content=system_prompt),
HumanMessage(content=prompt)
]
chat = AzureChatOpenAI(
azure_endpoint=endpoint,
api_version=api_version,
deployment_name=deployment,
azure_ad_token_provider=token_provider,
)
response = chat(messages)
return JSONResponse({"text": response.content})
Frontend (NiceGUI)
from nicegui import ui
def generate_story():
import httpx
try:
resp = httpx.post("http://localhost:8080/generate", json={})
story = resp.json().get("text", "No story generated.")
output_label.text = story
except Exception as e:
output_label.text = f"Error: {e}"
with ui.element('div').style('background-color: rgb(232 226 254);') \
.classes('flex items-center justify-center absolute top-0 left-0 full-width full-height'):
with ui.element('div').classes('full-width full-height flex flex-col items-center justify-center relative'):
ui.image('assets/home.jpg').props('fit=contain').classes(
'rounded-2xl w-full h-auto object-contain max-w-[95vw] max-h-[95vh]'
)
with ui.row().classes(
'absolute bottom-0 left-[10vw] right-[10vw] px-8 flex flex-col md:flex-row flex-wrap items-center justify-between z-10 mb-[8vh]'
):
ui.button(
'SURPRISE ME!',
on_click=generate_story
).classes(
'!bg-yellow-400 !hover:bg-yellow-400 text-white rounded-full '
'px-6 py-3 text-base font-bold flex items-center shadow-lg mr-4'
).props('icon=play_circle size=md')
output_label = ui.label("").classes("mt-6 text-lg")
ui.run()
Sicurezza e buone pratiche per la produzione
- HTTPS: Non esporre mai il tuo sito senza HTTPS. Usa Nginx come reverse proxy e un certificato SSL (per esempio Let’s Encrypt).
- Sanificazione e validazione: L’esempio usa una validazione robusta (lunghezza, caratteri consentiti, campi obbligatori) per evitare attacchi di injection e abusi.
- Reverse proxy (Nginx):
Esempio di base:
server {
listen 443 ssl;
server_name tu-dominio.com;
ssl_certificate /etc/letsencrypt/live/tu-dominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tu-dominio.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8081; # puerto NiceGUI
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
