The schedule showed a lecturer only where the session notes named one
(Globalizacja), so the student plan had nobody under the other courses.
The session JSON now carries the course lecturer in its course ref and an
optional per-session lecturer (new nullable column, admin field, seed key)
for courses shared between two people. The student app shows the
session's own lecturer or, failing that, the course's.
The management API could only upsert a material on its title and course,
so a renamed note became a second material. PUT /materials/{id} applies the
same body to one material (422 when the new title and course belong to
another one), GET /materials/{id} shows a draft or published material with
its public files, and the upsert, update and validation share one path.
The README documents the management API.
Centrifugo calls the proxy from 127.0.0.1 for every client, so the
per-IP fallback put every subscribe into one bucket that any token
holder could exhaust. Throttle only the token route, per user.
- Boot builds the realtime service and installs the user lookup
- the Centrifugo driver ships with the app; config keeps the null driver
- token and subscribe-proxy routes mount only with golem15.presentations,
the token route behind presentations.realtime-owner
- jz.bm.realtime per-user throttle bucket and websockets:health
Datepickers on native time.Time / lagoon.Date fields, file uploads on
materials, readings and class sessions managed from the course form
(course_id made nullable).
Overview, features, the API reference with every route and response
shape, admin screens, configuration, the bm:seed file format,
dependencies and testing.
Testcontainers Postgres, a fresh database per test, skipped under
-short: migrations up and down, schema constraints, bm:seed idempotency
and rollback, every API route through the real jwt.auth guard, and admin
saves through the admin API.
Validates a whole JSON file (course keys, meeting numbers, values, dates)
and upserts meetings, courses, class sessions, deadlines and readings in
one transaction, matched on their natural identity. Prints a created,
updated and unchanged summary; --dry-run only validates. Ships a small
example fixture with made-up data.
Dashboard, courses with counters, schedule, meetings, deadlines,
readings with per-user progress (mark and unmark) and paginated
published materials with tags and files, behind golem15.user's
jwt.auth. Responses are built field by field with Warsaw RFC 3339
timestamps; bad query parameters answer 422 in Laravel's shape.
Seven admin controllers in a "Studia BM" navigation group, guarded by
jz.bm.access_courses, jz.bm.access_schedule and jz.bm.access_materials,
with Polish and English labels. Date and time fields are checked by the
models before a save and answered as 422 on the field.
Courses, meetings, class sessions, deadlines, readings with per-user
progress, materials with tags and file attachments. Timestamps are
stored in UTC and held as Europe/Warsaw text so the admin text inputs
can write them.