diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d485eb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/__pycache__/ +/static/ +/venv/ +/pythagoras.tar.xz diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c97fea2 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: default +default: run + +include client/make.mk + +.PHONY: run +run: $(CLIENT_TARGETS) venv + source venv/bin/activate && python main.py + +.PHONY: build +build: $(CLIENT_TARGETS) + +.PHONY: pack +pack: pythagoras.tar.xz + +.PHONY: clean +clean: client_clean + rm -rf __pycache__ + rm -rf venv + rm -f pythagoras.tar.xz + +venv: + python -m venv venv + source venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt + +pythagoras.tar.xz: main.py $(CLIENT_TARGETS) + tar --transform='s|^|pythagoras/|' -Jcvf $@ $^ diff --git a/README.md b/README.md index c10968d..164f168 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,29 @@ Clone the repository: Install the dependencies: -`cd pythagoras` - -`python -m venv venv` - -`source venv/bin/activate` - -`pip install -r requirements.txt` +`sudo pacman -S python-virtualenv` ## Running the app -Simply run the main Python file to start the server: +Simply execute the **run** recipe in the Makefile to start the server: - `python main.py` +`make run` + +Note that run is also the default recipe. So `make` works too. + +The run recipe should take care of everything for you... enjoy! + +## Cleaning up the mess + +If you wish to go back to a clean slate just run: + +`make clean` + +## Packing for production + +To make an archive `pythagoras.tar.xz` in project root simply run: + +`make pack` ## Usage diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/client/bun.lockb b/client/bun.lockb new file mode 100755 index 0000000..f9c0643 Binary files /dev/null and b/client/bun.lockb differ diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..554c142 --- /dev/null +++ b/client/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/client/make.mk b/client/make.mk new file mode 100644 index 0000000..d40bcaa --- /dev/null +++ b/client/make.mk @@ -0,0 +1,33 @@ +CLIENT_PAGES := index.html +CLIENT_STYLES := style.css +CLIENT_SCRIPTS := script.js +CLIENT_TARGETS := $(CLIENT_PAGES:%=static/%) \ + $(CLIENT_STYLES:%=static/%) \ + $(CLIENT_SCRIPTS:%=static/%) + +.PHONY: client_clean +client_clean: + rm -rf static + rm -rf client/node_modules + +client/node_modules: + cd client && bun install + +static/%.html: client/%.html client/node_modules + @mkdir -p $(@D) + cat $< | \ + bun run --cwd client html-minifier \ + --collapse-inline-tag-whitespace \ + --collapse-boolean-attributes \ + --collapse-whitespace \ + --remove-attribute-quotes \ + --remove-comments \ + --remove-redundant-attributes > $@ + +static/%.css: client/%.scss client/node_modules + @mkdir -p $(@D) + bun run --cwd client sass $(notdir $<) --style compressed > $@ + +static/%.js: client/%.ts client/node_modules + @mkdir -p $(@D) + bun build $< --minify --outfile $@ diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..1ef9211 --- /dev/null +++ b/client/package.json @@ -0,0 +1,14 @@ +{ + "name": "client", + "type": "module", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "html-minifier": "^4.0.0", + "sass": "^1.87.0" + } +} diff --git a/client/script.ts b/client/script.ts new file mode 100644 index 0000000..e69de29 diff --git a/client/style.scss b/client/style.scss new file mode 100644 index 0000000..e69de29 diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/main.py b/main.py index eb86375..191e344 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,9 @@ from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect, BackgroundTasks -from fastapi.responses import JSONResponse +from fastapi.responses import HTMLResponse, JSONResponse import logging import uvicorn from typing import Dict, List, Any +from dataclasses import dataclass import json import httpx import asyncio @@ -43,6 +44,19 @@ class ConnectionManager: manager = ConnectionManager() +# Static files +def read_file(filepath: str) -> str: + with open(filepath, "r", encoding="utf-8") as f: + return f.read() + +@dataclass +class StaticFiles: + index_html: str = read_file("static/index.html") + +@app.get("/presentation/") +async def presentation_index(_: Request): + return HTMLResponse(status_code=200, content=StaticFiles.index_html) + # Endpoints @app.post("/control") async def control_endpoint(request: Request):