Files
librustui/README.md
T
2026-11-08 19:28:03 +01:00

145 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# librus-tui
A terminal-looking web frontend for **Librus Synergia**, built on the async
Synergia client from [dani3l0/librusik](https://github.com/dani3l0/librusik)
(`lib/api/librus.py`, stable branch).
The whole UI is a single-page phosphor-green "TUI" in the browser: keyboard
navigation, command bar, box-drawn panels, live clock, scanlines and an OAuth
boot log. The Python backend talks to Synergia and keeps your Librus cookies
server-side; the browser only gets an opaque session cookie.
```
_ ___ ____ ____ _ _ ____
| | |_ _| __ )| _ \| | | / ___|
| | | || _ \| |_) | | | \___ \
| |___ | || |_) | _ <| |_| |___) |
|_____|___|____/|_| \_\\___/|____/ // TUI
```
## Quick start
```bash
./run.sh # real Librus account, http://127.0.0.1:8765
./run.sh --demo # offline sample data, no Librus account needed
```
`run.sh` creates `.venv`, installs the two dependencies, and starts the server.
To run it manually:
```bash
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python app.py --demo
```
Then open <http://127.0.0.1:8765>. In demo mode use the
`[ enter demo mode ]` button; in real mode log in with your Synergia
credentials.
CLI options:
| option | default | description |
| --- | --- | --- |
| `--host` | `127.0.0.1` | bind address |
| `--port` | `8765` | HTTP port |
| `--demo` | off | use the offline dataset |
| `--debug` | off | verbose logs / access log |
Environment equivalents: `LIBRUS_HOST`, `LIBRUS_PORT`, `LIBRUS_DEMO=1`.
## Views
| key | view | contents |
| --- | --- | --- |
| `1` | dashboard | profile, lucky number, behaviour, counters, today's lessons, upcoming breaks |
| `2` | grades | weighted subject averages, colour-coded grade chips, comments, teacher, date |
| `3` | timetable | week grid, substitutions, cancellations, extra activities |
| `4` | homework | tests and homework sorted by date, full descriptions |
| `5` | attendance | register with absences, lateness and excused lessons |
| `6` | messages | inbox + reader + attachment downloads |
| `7` | free days | school/teacher free days and parent-teacher conferences |
### Keyboard
- `1`–`7` — switch view
- `j` / `k`, arrows — scroll; in messages move through the inbox
- `g` / `G` — top / bottom
- `r` — refresh
- `:` — command line (`:grades`, `:week 1`, `:reload`, `:logout`, …)
- `?` — help
- `/` — filter grades (grades view)
- `[` / `]` — previous / next week (timetable view)
- `q` — log out
- `Esc` — close modal / leave command line
## How it works
```
browser ── fetch /api/* ──> aiohttp app ── HTTPS ──> synergia.librus.pl
│ │
│ session cookie only ├─ librus.py (OAuth + gateway API + message scraping)
└──────────────────────────┘
```
1. `POST /api/login` performs the same flow as librusik: Synergia portal
session, `api.librus.pl/OAuth/Authorization?client_id=46`, the 2FA redirect,
then `Auth/TokenInfo` / `Auth/UserInfo` to verify the resulting cookies.
2. Cookies (`DZIENNIKSID`, `SDZIENNIKSID`, …) stay in an in-memory `Librus`
object on the server. The browser gets a random `librustui_session` cookie.
3. `/api/*` endpoints wrap the client methods from librusik and return JSON.
Message HTML is sanitised with BeautifulSoup before it reaches the browser.
4. Sessions expire after 12 h idle / 24 h absolute; restarting the server
invalidates all of them.
### Files
```
app.py aiohttp server, sessions, API endpoints, sanitizer
librus.py async Synergia client (adapted from librusik, MIT)
demo.py offline dataset behind --demo
static/index.html TUI shell
static/style.css phosphor terminal theme
static/js/00-core.js state, helpers, API wrapper, chrome
static/js/10-auth.js login, session chrome, clock
static/js/20-dashboard.js
static/js/22-grades.js
static/js/24-timetable.js
static/js/26-more-views.js
static/js/30-app.js navigation, commands, keyboard, bootstrap
```
## Security notes
- Bind address defaults to `127.0.0.1`, not `0.0.0.0`.
- Credentials are sent from the browser to this server, then to Librus; they
are **not** logged or written to disk.
- Session cookies are `HttpOnly`, `SameSite=Lax`. Responses set a strict CSP,
`X-Frame-Options: DENY`, `nosniff` and `no-referrer`.
- Cross-origin `POST`s are rejected by an `Origin` check.
- Message content is treated as untrusted and sanitised server-side (scripts,
event handlers, iframes and unsafe URL schemes are removed).
- If you expose this on the internet, put it behind HTTPS (e.g. Caddy/nginx),
set `--host 127.0.0.1`, and consider adding authentication in front of it.
## Limitations
- Librus has no public API contract. If Synergia changes its markup or gateway
responses, parts can break; the UI shows a "signal lost" panel with the
upstream error instead of crashing.
- The messages reader scrapes the HTML portal, exactly like librusik, so it is
the most fragile view.
- Attachments are proxied through the server and require the original session
cookies to still be valid.
- One process keeps sessions in memory; there is no multi-user persistence.
Restart = everyone logs in again.
## Credits
Client flow, endpoints and scraping logic are adapted from
[dani3l0/librusik](https://github.com/dani3l0/librusik), MIT licensed,
copyright © 2022 dani3l0.
This project is unofficial and not affiliated with Librus. Use it with your own
account at your own risk.