Crank dat!

This commit is contained in:
purplebored committed 2026-11-08 19:28:03 +01:00
commit fe13a4d989
17 files changed
+6624

No files matched your search

+144
View File
@@ -0,0 +1,144 @@
# 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.