Skip to content

Instantly share code, notes, and snippets.

@body20002
Last active August 22, 2022 18:36
Show Gist options
  • Save body20002/22f60cb6fe90303306bfe3a0ff61115f to your computer and use it in GitHub Desktop.
Save body20002/22f60cb6fe90303306bfe3a0ff61115f to your computer and use it in GitHub Desktop.
FJPT Stack (FastAPI + Jinja2 + Preact + tailwindcss)
<!doctype html>
<html>
<head>
{% block head %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{{ url_for('static', path='css/style.css') }}" rel="stylesheet">
<title>{% block title %}
{{ '%s | Your Name' % title or 'Your Name' }}
{% endblock %}
</title>
<script>
function connect(){
var timeout = 250;
var ws = new WebSocket("ws://localhost:8000/dev")
ws.onclose = function(event){
setTimeout(connect, timeout);
timeout += timeout;
};
ws.onmessage = function(event) {
const data = event.data;
if (data == 'reload'){
ws.send("close")
window.location.reload();
}
};
};
connect();
</script>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
{% extends "base.html" %}
{% block body %}
<h1 class="text-3xl font-bold underline">
Hello world!
</h1>
<script type="module">
import { html, render } from 'https://unpkg.com/htm/preact/index.mjs?module'
const CustomApp = () => {
return html`<button onClick=${() => console.log("Hello, World!")}>
Click ME
</button>`;
}
render(html`<${CustomApp} />`, document.getElementById("custom-app"))
</script>
<span id="custom-app"></span>
{% endblock %}
import asyncio
import contextlib
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect, status
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
APP_DIR = Path(__file__).resolve()
TEMPLATES_DIR = APP_DIR / "templates"
app = FastAPI()
app.mount("/static", StaticFiles(directory=TEMPLATES_DIR / "static"), name="static")
templates = Jinja2Templates(directory=TEMPLATES_DIR)
@app.get("/", response_class=HTMLResponse)
def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.exception_handler(status.HTTP_404_NOT_FOUND)
def not_found(request: Request, _):
return templates.TemplateResponse("404.html", {"request": request, "title": "404 Not Found"})
@app.exception_handler(status.HTTP_500_INTERNAL_SERVER_ERROR)
def internal_server_error(request: Request, _):
return templates.TemplateResponse("500.html", {"request": request})
class Handler(FileSystemEventHandler):
def __init__(self, websocket: WebSocket) -> None:
super().__init__()
self.ws = websocket
def on_modified(self, _):
asyncio.run(self.ws.send_text("reload"))
@app.websocket("/dev")
async def reload_files(websocket: WebSocket):
event_handler = Handler(websocket)
observer = Observer()
observer.schedule(event_handler, TEMPLATES_DIR, recursive=False)
observer.schedule(event_handler, TEMPLATES_DIR / "static", recursive=True)
observer.schedule(event_handler, TEMPLATES_DIR / "tailwind/src", recursive=True)
observer.start()
await websocket.accept()
with contextlib.suppress(RuntimeError):
try:
with contextlib.suppress(WebSocketDisconnect):
while (await websocket.receive_text()) != "close":
pass
observer.stop()
finally:
with contextlib.suppress(WebSocketDisconnect):
await websocket.close(code=status.WS_1000_NORMAL_CLOSURE)
observer.join()
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", port=8000, reload=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment