Crank dat!
This commit is contained in:
17 files changed
+6624
No files matched your search
@@ -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.
|
||||
Reference in new issue
Block a user