HTMX & Nginx
Little Test with HTMX & Nginx
recently, i saw the Keynote - “Full-Stack Python” (Andy “Pandy” Knight) and i read an article about html & websockets. So I thought why not give it a try?
Preview
Requirements
the usual stuff:
- Virtual Machine (here: OpenBSD VM)
- FQDN Pointing to your Box
- SSL Cert
Webroot
on your webserver, create a new webroot wherever you have your pages located.
su - webmaster
mkdir -p /var/www/virtual/your.page.de
cd /var/www/virtual/your.page.de/
main.py
the main part of the python code …
cat << 'EOF' > main.py
from datetime import datetime
from fastapi import FastAPI, Request, WebSocket
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory="templates")
app = FastAPI()
@app.get("/")
async def get(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.websocket("/ws")
async def time_handler(websocket: WebSocket):
await websocket.accept()
# response content
content = """
<div hx-swap-oob="beforeend:#content">
{time}: {message}<br>
</div>
"""
while True:
msg = await websocket.receive_json()
#content.format(time=time.time(), message=msg["chat_message"])
now = datetime.now()
time = now.strftime("%H:%M:%S")
await websocket.send_text(
content.format(time=time, message=msg["chat_message"])
)
EOF
index.html
we need one webpage in the template folder
mkdir -p templates
cat << 'EOF' > templates/index.html
<!DOCTYPE html>
<html>
<head>
<!-- include htmx -->
<script
src="https://unpkg.com/[email protected]"
crossorigin="anonymous"
></script>
</head>
<body>
<h1>Hello world</h1>
<!-- websocket connection -->
<div hx-ws="connect:/ws">
<!-- input for new messages, send a message to the backend
via websocket -->
<form hx-ws="send:submit">
<input name="chat_message" />
</form>
</div>
<!-- location for new messages from the server -->
<br>
<div id="content"></div>
</body>
</html>
EOF
pyproject.toml
cat << 'EOF' > pyproject.toml
[tool.poetry]
name = "your.project.de"
version = "0.1.0"
description = ""
authors = ["<[email protected]>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.95.1"
uvicorn = "^0.21.1"
jinja2 = "^3.1.2"
websockets = "^11.0.2"
wsproto = "^1.2.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
EOF
create virtualenv
poetry shell
poetry install
nginx.conf
we need a reverse proxy in front of the application. nginx will terminate tls and do the logging
change to nginx vhosts directory
cd /etc/nginx/sites
add config
cat << 'EOF' > your.page.de.conf
upstream websocket {
server 127.0.0.1:8123;
}
# Redirect to https
server {
listen 80;
listen [::]:80;
server_name your.page.de;
location / {
return 301 https://$host$request_uri;
}
}
# WebServer
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name your.page.de;
access_log /var/log/nginx/your.page.de-access.log;
error_log /var/log/nginx/your.page.de-error.log;
ssl_certificate_key /etc/ssl/certs/your.domain.de/your.domain.de.key;
ssl_certificate /etc/ssl/certs/your.domain.de/fullchain.cer;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains;";
location / {
proxy_pass http://127.0.0.1:8123;
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 https;
}
location /ws {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
EOF
you need to adapt a few things. Path, Cert, FQDN …
Restart Services
afterwards, start / restart the nginx service …
rcctl restart nginx
run app
… and start the app from the shell
poetry run python3 -m uvicorn main:app --port=8123 --reload
open your browser https://your.page.de
Any Comments ?
sha256: 0bbbad50481c17422697f333762f623ce1363607dcd4b50ec11430eab1e0a109