Implement invite-token registration with optional email delivery, add admin UI actions for creating invites and opening user workspaces, and support superuser workspace override while preserving per-user code isolation with shared read-only lib. Made-with: Cursor
149 lines
5.8 KiB
Markdown
149 lines
5.8 KiB
Markdown
# python-editor
|
|
|
|
Browser-based Python editing: **FastAPI** serves static assets, stores workspace files, and optional **API key auth**. **Pyodide** runs your scripts and **Jedi** (inside Pyodide) powers completions and syntax diagnostics — no server-side Python execution or LSP process.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
cp .env.example .env # optional: set WORKSPACE_ROOT, EDITOR_API_KEY, etc.
|
|
pipenv install
|
|
pipenv run dev
|
|
```
|
|
|
|
Configuration is read from **`.env`** at the repo root (see `.env.example`). Values there are applied when the app loads unless the variable is already set in your shell. [Pipenv](https://pipenv.pypa.io/) also loads `.env` for `pipenv run` commands.
|
|
|
|
Tests (includes **pytest** and **selenium** in dev dependencies):
|
|
|
|
```bash
|
|
pipenv run test
|
|
pipenv run test-integration # Playwright; optional
|
|
```
|
|
|
|
### Selenium
|
|
|
|
Selenium talks to a **real browser** against a **running server** (not the in-process `TestClient`).
|
|
|
|
1. Install **Google Chrome** or Chromium on the machine (Selenium 4 uses [Selenium Manager](https://www.selenium.dev/documentation/selenium_manager/) to resolve a matching driver).
|
|
2. In one terminal, start the app (default `http://127.0.0.1:8080`):
|
|
|
|
```bash
|
|
pipenv run dev
|
|
```
|
|
|
|
3. In another terminal:
|
|
|
|
```bash
|
|
pipenv run test-selenium
|
|
```
|
|
|
|
If the app listens elsewhere, set **`SELENIUM_BASE_URL`** (e.g. `http://127.0.0.1:9000`) before running.
|
|
|
|
Or run only Selenium-marked tests:
|
|
|
|
```bash
|
|
cd src && PYTHONPATH=. pipenv run pytest ../tests -m selenium -v
|
|
```
|
|
|
|
If nothing is listening, the smoke test **skips** with a short message instead of failing.
|
|
|
|
Open [http://localhost:8080](http://localhost:8080).
|
|
|
|
### Editor runtime controls
|
|
|
|
- `Run Python` runs the active open `.py` tab.
|
|
- Enable `Run main.py` to always run `code/main.py` instead.
|
|
- Pressing `Run Python` while a script is running will stop and restart with the selected target.
|
|
- `LSP` badge in the header shows in-browser Jedi syntax status (`n/a`, `checking...`, `OK`, or issue count).
|
|
|
|
## Deploy with Docker
|
|
|
|
Build and run with Docker Compose:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
mkdir -p data
|
|
docker compose up --build
|
|
```
|
|
|
|
Then open [http://localhost:8080](http://localhost:8080).
|
|
|
|
Notes:
|
|
|
|
- `workspace/` is mounted to `/app/workspace` so your code persists locally.
|
|
- `data/` is mounted to `/app/data` for the SQLite auth DB.
|
|
- In container mode, `WORKSPACE_ROOT` and `AUTH_DATABASE_PATH` are set by `docker-compose.yml`.
|
|
|
|
**User accounts** — Set `AUTH_ENABLED=true` in `.env` to require sign-in for workspace APIs. Users live in a SQLite file (`AUTH_DATABASE_PATH`, default `./data/editor.db`). Use `/register` (if `AUTH_REGISTER_OPEN=true`) or `BOOTSTRAP_ADMIN_USERNAME` / `BOOTSTRAP_ADMIN_PASSWORD` for the first superuser. Superusers can **GET/POST/DELETE `/api/users`** to list, create, or remove accounts.
|
|
|
|
Email invite signup:
|
|
|
|
- Superusers can create invites via `POST /api/users/invites` with `{ "email": "...", "expires_days": 7 }`.
|
|
- Response includes `invite_url`; if SMTP is configured the invite email is sent automatically.
|
|
- Set `AUTH_INVITE_ONLY=true` to require invite tokens for all registrations.
|
|
- Registration page accepts invite links like `/register?invite=<token>`.
|
|
|
|
When auth is enabled, file APIs use a per-user workspace under `WORKSPACE_ROOT/users/<username-id>/` for **isolated `code/`**. The `lib/` tree is shared and read-only for all users. When auth is disabled, the shared workspace root is used for everything.
|
|
|
|
Admins can open another user's workspace from the home page user management panel (links to `/editor?workspace_user_id=<id>`). Only superusers may use this override.
|
|
|
|
**API key** — If `EDITOR_API_KEY` is set, requests may use `Authorization: Bearer …` instead of a session (useful for automation). When `AUTH_ENABLED=true`, a valid session *or* API key is accepted.
|
|
|
|
The home page can store the API key in `sessionStorage` when you are not using cookie login, or use `?api_key=` on `/editor`.
|
|
|
|
## Layout
|
|
|
|
- `src/` — FastAPI app and static UI (`src/static/`)
|
|
- `workspace/` — default tree: `code/` (editable), `lib/` (read-only via API)
|
|
|
|
## ESP32 / NeoPixel mock
|
|
|
|
The browser runtime now includes MicroPython-style mocks in `workspace/lib`:
|
|
|
|
- `machine.Pin`
|
|
- `neopixel.NeoPixel`
|
|
|
|
Use them from scripts in `workspace/code` exactly like ESP32 examples:
|
|
|
|
```python
|
|
from machine import Pin
|
|
import neopixel
|
|
|
|
np = neopixel.NeoPixel(Pin(4), 8)
|
|
np[0] = (255, 0, 0)
|
|
np.write()
|
|
```
|
|
|
|
`write()` updates the NeoPixel simulator so you can verify behavior visually.
|
|
|
|
Simulator modes:
|
|
|
|
- Default: in-app LED strip/panel section under the editor.
|
|
- `16x16 panel` checkbox: opens a dedicated popup with 16x16 serpentine mapping:
|
|
- first LED at top-right
|
|
- first row goes right -> left
|
|
- rows zig-zag left/right.
|
|
- The 16x16 popup closes automatically on **Stop** or when script execution finishes.
|
|
|
|
Tutorial files:
|
|
|
|
- `LED_TUTORIAL.md` - step-by-step NeoPixel tutorial
|
|
- `workspace/code/led_tutorial.py` - runnable guided LED example
|
|
- `workspace/code/led_patterns.py` - reusable pattern helpers (`rainbow_frame`, `chase_frame`, `twinkle_frame`)
|
|
- `workspace/code/pattern_rainbow_demo.py` - rainbow animation demo
|
|
- `workspace/code/pattern_chase_demo.py` - chase animation demo
|
|
- `workspace/code/pattern_twinkle_demo.py` - twinkle animation demo
|
|
- `workspace/code/panel16_utils.py` - helpers for 16x16 serpentine mapping
|
|
- `workspace/code/panel16_rainbow_wave.py` - 16x16 rainbow wave
|
|
- `workspace/code/panel16_bounce.py` - 16x16 bouncing pixel with trail
|
|
- `workspace/code/panel16_matrix_rain.py` - 16x16 matrix rain effect
|
|
|
|
## Dev auto-reload hook
|
|
|
|
Project hook files are included in `.cursor/`:
|
|
|
|
- `.cursor/hooks.json`
|
|
- `.cursor/hooks/dev-reload-touch.sh`
|
|
|
|
When files are edited through Cursor tools, the hook updates `src/static/.reload-token`.
|
|
The editor (on localhost) polls that token and auto-reloads the browser when it changes.
|