Files
studipsync/AGENTS.md
2025-11-14 16:02:27 +01:00

150 lines
6.9 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.
# StudIP Sync Agent
## Goal
Implement a command-line tool in Rust that performs a **one-way sync** of files from Stud.IP (JSON:API at Uni Trier) to the local filesystem. [web:68]
The local directory structure must be: `<semester>/<course>/<studip-folders>/<files>`. [web:88]
## Environment
- Target OS: Linux (Arch, follow XDG base directory conventions). [web:135]
- Language: Rust (2021 edition).
- Use `reqwest` + `tokio` for HTTP and async, `serde` for JSON and TOML, and standard Rust CLI patterns. [web:111][web:131]
- Build as a single binary named `studip-sync`.
## Code Quality: Formatting and Linting
- Use `rustfmt` as the standard formatter for all Rust code; code must be kept `cargo fmt` clean. [web:144][web:148]
- Use `clippy` as the linter; the project must pass `cargo clippy --all-targets --all-features -- -D warnings` with no warnings. [web:144][web:149][web:159]
- Add a `rustfmt.toml` and (optionally) a `clippy.toml` where needed, but prefer default settings to stay idiomatic. [web:144][web:151]
- If CI is present, include steps that run `cargo fmt --all -- --check`, `cargo clippy --all-targets --all-features -- -D warnings`, and `cargo test`. [web:147][web:150][web:159]
## API
- Base URL: configurable, default `https://studip.uni-trier.de`. [web:68]
- JSON:API root: `<base_url>/jsonapi.php/v1`. [web:68]
- Authentication: HTTP Basic (username/password), encoded once as base64 and stored in TOML config. [web:1][web:118]
- Use JSON:API routes such as:
- `GET /users/me` to resolve the current user and related links (courses, folders, file-refs). [web:68][web:106]
- `GET /users/{user_id}/courses` to list enrolled courses. [web:85]
- Course-specific routes for folders and documents/file-refs, using the documented JSON:API routes for Stud.IP (e.g. `/courses/{course_id}/documents`). [web:88][web:93]
## Configuration (TOML, including paths)
All configuration and state in this project must use **TOML**. [web:131]
- Primary config file: XDG-compliant, e.g. `~/.config/studip-sync/config.toml`. [web:131][web:135]
- Example `config.toml` keys:
```
base_url = "https://studip.uni-trier.de"
jsonapi_path = "/jsonapi.php/v1"
# Authorization header value without the "Basic " prefix, base64("username:password").
basic_auth_b64 = "..."
# Local base directory for synced files.
download_root = "/home/<user>/StudIP"
# Maximum concurrent HTTP downloads.
max_concurrent_downloads = 3
```
- The `download_root` directory determines where the tool creates `semester/course/folders/files`. [web:68]
- The config file must be created with mode `0600` and never contain anything except necessary settings and the base64-encoded credential. [web:118][web:122]
### Credentials and auth
- On first run (or when running `studip-sync auth`), prompt interactively for username and password. [web:118]
- Construct `username:password`, base64-encode it, and store the result as `basic_auth_b64` in `config.toml`. [web:1][web:118]
- At runtime, send `Authorization: Basic <basic_auth_b64>` on all JSON:API requests. [web:1][web:68]
- Never log or print the password, `basic_auth_b64`, or full `Authorization` header. [web:118][web:128]
- On HTTP `401` or `403` from a known-good endpoint like `/users/me`, treat this as auth failure:
- Non-interactive runs: exit with a non-zero code and a clear message asking the user to run `studip-sync auth`. [web:118]
- Interactive runs: optionally prompt again and update `basic_auth_b64`.
## State (TOML as well)
- State file must also be TOML, stored under XDG data dir, e.g. `~/.local/share/studip-sync/state.toml`. [web:131][web:135]
- State is non-secret cached data:
```
user_id = "cbcee42edfea9232fecc3e414ef79d06"
[semesters."ws2526"]
id = "830eb86ad41d8f695d016647d557218a"
title = "Wintersemester 2025/26"
[semesters."ss25"]
id = "..."
title = "Sommersemester 2025"
[courses."830eb86a-...-course-id"]
name = "Rechnerstrukturen - Übung"
semester_key = "ws2526"
last_sync = "2025-11-14T12:34:56Z"
```
- The tool should:
- Cache `user_id` after the first successful `/users/me` call. [web:68][web:106]
- Cache semester IDs and human-readable keys (`ws2526`, `ss25`) after discovering them via JSON:API. [web:68]
- Optionally store course and last-sync metadata to reduce API calls (e.g. using `filter[since]` if supported). [web:88][web:93]
## Directory structure
- All downloads must go under `download_root`, respecting:
`download_root/<semester_key>/<course_name>/<studip_folder_path>/<file>`.
- `semester_key` is resolved from the state file (`ws2526`, `ss25`, etc.). [web:68]
- `course_name` and Stud.IP folder/file names should be normalized to safe filesystem paths (handle spaces, umlauts, and special characters) while staying human-readable. [web:68][web:104]
## Sync semantics
- One-way sync: Stud.IP → local filesystem only; never upload or modify data on Stud.IP. [web:68]
- Default behavior:
- Create directories and download new or changed files under `download_root`.
- Never delete local files by default.
- Provide optional flags:
- `--prune`: delete local files that no longer exist on Stud.IP.
- `--dry-run`: print planned actions (creates/downloads/deletes) without modifying the filesystem.
## Minimizing API usage and load
- Use cached `user_id` and semester mappings from `state.toml` to avoid repeated discovery calls. [web:68]
- When listing course documents, use JSON:API pagination and any available filters (e.g. `filter[since]`) supported by Stud.IPs document routes. [web:88][web:93]
- Avoid re-downloading unchanged files by checking JSON:API attributes such as ID, size, and modification time against the stored state. [web:93][web:106]
## CLI interface
- Binary name: `studip-sync`.
- Subcommands:
- `studip-sync auth`: set or update credentials; writes `config.toml`.
- `studip-sync sync`: perform sync from Stud.IP to `download_root`.
- `studip-sync list-courses`: list known courses with semester keys and IDs from state (refreshing if needed).
- Use standard exit codes:
- `0` on success.
- Non-zero on errors (auth failure, network error, JSON parse error, filesystem failure). [web:118]
## Performance & safety
- Limit concurrent HTTP requests (configurable via `max_concurrent_downloads`, default 3). [web:68]
- Stream file downloads directly to disk; do not load entire files into memory. [web:88]
- Handle HTTP and I/O errors gracefully with clear messages and without panicking.
- Keep dependencies minimal and use idiomatic Rust project structuring for maintainability. [web:136][web:137]
## Extensibility
- Internally, separate concerns into modules:
- `config` (TOML load/save for config and state).
- `studip_client` (JSON:API HTTP client).
- `sync` (sync logic and directory mapping).
- `cli` (argument parsing, subcommands). [web:136][web:137]
- Represent core entities as Rust types: `Semester`, `Course`, `Folder`, `FileRef`. [web:68][web:93]
- Design so that a future `MoodleProvider` can implement the same internal traits (e.g. `LmsProvider`) without changing the CLI surface.