Commit Graph

122 Commits

Author SHA1 Message Date
Jakub Zych
bff92cca78 test: cover material pins against postgres 2026-11-09 13:15:44 +01:00
Jakub Zych
341f6cd95a feat: add the material pin MCP tools
bm_pin_material and bm_unpin_material on the token twins (scope write), with the pin routes documented in the README.
2026-11-09 13:14:37 +01:00
Jakub Zych
9436fb62d6 feat: personal pins on materials
A per-user jz_bm_material_pins table and PUT/DELETE /materials/{id}/pin
on the student API and its token twins: 204 for a material the caller
may see, idempotent, 404 otherwise. List rows, course rows and the
detail carry pinned. The plain list lists the caller's pins first
(newest pin first); with q the relevance order stays. A pin writes only
the pins table and never changes the material's revision.
2026-11-09 13:13:54 +01:00
Jakub Zych
b8b46be398 build: rebuild the student app (tag type-ahead in the editor) 2026-11-09 12:40:54 +01:00
Jakub Zych
7c49f9283c build: rebuild the student app (note editor, tables and images, conflicts) 2026-11-09 02:04:29 +01:00
Jakub Zych
8bef2d7acf feat: material managers get a realtime token
- jz.bm.realtime-access admits holders of jz.bm.access_materials
  (Assumption A5), so every editor can follow materials:{id}
- README documents the materials channel, event and authorizer
2026-11-09 01:14:56 +01:00
Jakub Zych
2a47186a7a feat: announce material revisions on materials:{id}
- every revision bump (API writes and /bmadmin saves) enqueues
  updated.jz.bm.material with {material_id, revision} in a savepoint
- the materials namespace authorizer allows a subscribe only to a
  visible material the user may manage
- api.ManagesMaterials and api.CanWatchMaterial
2026-11-09 01:12:49 +01:00
Jakub Zych
40e4a2438b feat: tags, unpublish and image upload for the author
- GET /_bm/api/v1/tags: the jz_bm_tags dictionary, to material managers
- POST /materials/{id}/unpublish: back to drafts, no revision bump, the
  search document resynced
- POST /materials/{id}/images: one JPEG/PNG/GIF/WebP per request through
  storeMaterialUpload, refusals with reason type, size or count
- storeMaterialUpload/fileRefusalFor: attach.ErrNotImage is a type
  refusal, not a 500; storeMaterialFile keeps its texts
- the author's routes get their own per-user buckets (the inline
  throttle form shares one counter per user)
- README: the author routes and revision on the student detail
2026-11-09 01:05:54 +01:00
Jakub Zych
7bf18de987 feat: the author edits a material in the app
- PUT /_bm/api/v1/materials/{id} (jwt.auth only, no token twin): every
  key of the edit body required, revision checked on the row locked FOR
  UPDATE, 409 with the student detail of the server version
- editableMaterialForCaller: 404 for what the caller cannot see, 403 for
  what they see but may not manage
- tags from the dictionary only (at most 4), http(s) urls, the body
  normalised as bm-notatka push does; course, author and publication kept
- loadMaterialDetail shared by GET /materials/{id} and the edit answers
2026-11-09 01:01:25 +01:00
Jakub Zych
263c9c3926 feat: revision-check the upsert, MCP and admin writes
- POST /materials locks the matched row and answers 409 to a stale
  revision when it updates; a create ignores revision
- bm_manage_save_material and bm_manage_update_material take revision
- /bmadmin saves bump the revision on content changes (not checked)
- document revisions and the 409 in the README
2026-11-08 23:36:17 +01:00
Jakub Zych
237069c827 feat: revision-check material writes
- add jz_bm_materials.revision (default 1), read-only for GORM
- bump it only on content changes (models.ReviseMaterial)
- PUT /materials/{id} locks the row and answers 409 to a stale revision
- management answers carry revision and updated_at, the student detail
  carries revision
2026-11-08 23:33:34 +01:00
Jakub Zych
94ca257c6f build: rebuild the student app (materials search, zjazd filter, share input, print) 2026-11-08 20:34:17 +01:00
Jakub Zych
54300de94c docs: tags hold topics, the zjazd is meeting_id
The backend materials list shows the zjazd (meeting, by number). The
MCP save and update material tools, the README material example and the
management token test stop modelling a zjazd as a tag, and the README
describes the one-way zjazd tag migration.
2026-11-08 20:30:29 +01:00
Jakub Zych
7cea8ab318 feat: move zjazd tags to the material's meeting
The one-way migration 20261108140000_move_zjazd_tags_to_meetings gives
every material tagged zjazd-N without a meeting meeting N (the lowest
when it has several), never overwrites a meeting_id, logs tags naming a
missing meeting or disagreeing with meeting_id at Warn, and deletes the
exact zjazd-N tags; their links go through ON DELETE CASCADE. Rollback
is a no-op: the backup is the pg_dump taken before migrate.
2026-11-08 20:29:31 +01:00
Jakub Zych
f841e8ed71 feat: extract file text on every attach path and backfill on reindex
- inline and signed uploads queue the text job in their transaction
- the admin form queues it in the save transaction
- detach resyncs the material (its text row goes by cascade)
- bm:materials-reindex backfills missing PDF texts before each sync
2026-11-08 20:16:14 +01:00
Jakub Zych
412821ec56 feat: index the text of attached PDFs
- jz_bm_material_file_texts caches the pdftotext output per file
- the River job jz.bm.extract_material_text extracts and resyncs
- the management multipart attach queues it inside its transaction
- file_texts in the document and a file snippet with its name
2026-11-08 20:11:19 +01:00
Jakub Zych
dd13795e6f feat: search the management list and the MCP material tools
- GET /api/v1/bm/materials takes q: candidates over every material, in
  rank order, with the snippet of the student list; 503 when the engine
  cannot answer
- bm_materials and bm_manage_list_materials take q; under MCP the snippet
  is plain text with ellipses where text was cut
- MCPMaterialPrivacy no longer claims no file text is ever returned
- README documents q, the snippet, the 503, the Search section and
  bm:materials-reindex
2026-11-08 19:53:26 +01:00
Jakub Zych
ffd19342b2 feat: keep the materials index in sync and add bm:materials-reindex
- the materials admin syncs after create and update and removes after
  delete, once the save transaction commits (UseMaterialIndex, set at Boot)
- POST and PUT /api/v1/bm/materials sync after the transaction, DELETE
  removes the document (its statement has no primary key, so no callback)
- bm:materials-reindex [--fresh] [--if-configured] ensures the index and
  syncs every material; it refuses without an engine and runs nightly
  at 04:15 with --if-configured
- TestMaterialSearchAgainstDatabase (Postgres) is written for the final
  verification phase
2026-11-08 19:50:50 +01:00
Jakub Zych
4ffce6e6ac feat: search materials through beachcomber
- Material is a beachcomber Searchable (index jz_bm_materials) in the
  hand-written models/material_search.go; jz.bm requires summercms v0.6.4
- classes.MaterialIndexer asks Typesense for up to 250 candidates with
  body highlights; the app-wide Gate is on only for jz.bm-marked contexts
- GET /_bm/api/v1/materials takes q: candidates are re-gated, counted and
  paged in SQL in rank order, each row with at most one plain snippet
- search failure answers 503 Search is unavailable.; without q the list
  is unchanged
2026-11-08 19:45:46 +01:00
Jakub Zych
f7c400756f build: rebuild the student app (private materials) 2026-11-08 11:58:11 +01:00
Jakub Zych
3346cb6ff6 feat: manage material visibility and shares
The management save/update body takes is_private, author_user_id and
share_user_ids (absent keeps each; a new material gets the caller as
author), and the managed material shows them. New routes PATCH
/materials/{id}/visibility, PUT /materials/{id}/shares and GET /users,
with the MCP tools bm_manage_set_material_visibility,
bm_manage_set_material_shares and bm_manage_list_users. The /bmadmin
form gets the private switch, the author picker and shared_with.
2026-11-08 11:56:14 +01:00
Jakub Zych
418f8240a4 feat: private materials with per-user shares
A material may be private: only its author and the users it is shared
with see it. Every student read (list, detail, course detail and
counters, the checklist linked material, the token twins) goes through
one visibility fragment. Existing materials stay public with no author.

GET/PUT /_bm/api/v1/materials/{id}/access let the author, a material
manager, toggle the flag and pick the users; the first change of an
author-less material makes the caller its author.
2026-11-08 11:52:47 +01:00
Jakub Zych
5ed02ed9f7 build: rebuild the student app (theme switch, print styles) 2026-11-07 20:48:40 +01:00
Jakub Zych
dbd64aca33 build: rebuild the student app (delete a deck, Pilot first) 2026-11-07 19:18:22 +01:00
Jakub Zych
cdc116c145 build: rebuild the student app (mobile tab bar order) 2026-11-07 15:57:44 +01:00
Jakub Zych
89772fa020 build: rebuild the student app (cast status to the casting phone only) 2026-11-07 14:37:26 +01:00
Jakub Zych
ef5155b0ab build: rebuild the student app (cast player mounted before start) 2026-11-07 13:39:01 +01:00
Jakub Zych
2691dca004 build: rebuild the student app (cast keep-awake as in HA) 2026-11-07 13:20:59 +01:00
Jakub Zych
aed08bbe09 build: rebuild the student app (cast keep-awake) 2026-11-07 12:43:13 +01:00
Jakub Zych
7c0f8de31b build: rebuild the student app (cast cover stays down) 2026-11-07 12:34:42 +01:00
Jakub Zych
018cd5537e build: rebuild the student app (cast receiver diagnostics) 2026-11-07 11:57:00 +01:00
Jakub Zych
d6dbcb48bf build: rebuild the student app (cast receiver and cast mode) 2026-11-07 08:35:13 +01:00
Jakub Zych
de1e4624bc build: deck export to PPTX 2026-11-06 15:05:55 +01:00
Jakub Zych
a27a16243d build: robots meta and robots.txt (noindex, Disallow) 2026-11-05 01:59:53 +01:00
Jakub Zych
0dd01855fa build: deck print to PDF, section-slide speaker panels fix 2026-11-03 21:07:15 +01:00
Jakub Zych
a075963c4c Checklisty: procent postępu liczony wg szacowanego czasu 2026-10-30 18:07:36 +01:00
Jakub Zych
9677e58288 fix: answer an empty checklist with an empty items list 2026-10-30 13:36:09 +01:00
Jakub Zych
d894515da1 chore: rebuild the frontend with checklists 2026-10-30 12:41:04 +01:00
Jakub Zych
d7cd5baedf feat: private checklists for zjazd preparation
Per-user checklists with items that may link a course, reading, deadline,
material or own presentation deck. An item with a reading shares the
reading's read mark. Student API routes with token twins, MCP tools,
migrations, tests and README.
2026-10-30 12:41:04 +01:00
Jakub Zych
643f8cbf7c chore: rebuild the frontend with the sztab deck theme 2026-10-30 00:04:25 +01:00
924e6aa0dc feat: signed one-time uploads for material files
- classes/uploads.go: tickets signed with HMAC-SHA256 under the
  jz.bm.upload.v1: label (app.key), 10-minute expiry, the nonce pulled
  from the cache once (duplicated from golem15.presentations on purpose)
- POST /api/v1/bm/materials/{id}/files/upload-url issues the URL with a
  curl example; POST /_bm/api/v1/uploads/{ticket} takes one file part,
  no bearer, throttled per IP and per ticket, user and permission
  re-checked, stored through storeMaterialFile
- bm_manage_create_material_upload; README documents the file routes
2026-10-28 15:10:32 +01:00
ce6ac0a78e test: expect signed one-time material uploads
- TestUploadTicket: HMAC under the jz.bm.upload.v1: label, expiry,
  malformed and foreign tickets, single use through the cache
- TestSignedMaterialUploadRefusesBadTickets, TestCreateMaterialUploadNeedsCaller
- TestRoutes, TestBMMCP and TestManageBucketKey expect the upload-url
  route, the uploads route with its two buckets and the upload tool
- compile-only stubs
2026-10-28 15:07:30 +01:00
93066c63ea feat: attach material files over MCP as base64
- storeMaterialFile: the one attach.Store call of the management API,
  shared by the multipart route and the new inline route
- POST /api/v1/bm/materials/{id}/files/inline: JSON {name, mime?,
  content_base64}, at most 3000000 bytes decoded, body.limit 4300000
- bm_manage_attach_material_file and bm_manage_detach_material_file,
  both warning that material files are public to anyone with the link
2026-10-28 15:03:43 +01:00
a0aaeba0b7 feat: add the BM management MCP tools
- 28 typed tools over /api/v1/bm: list, show, create and update for
  courses and meetings, plus delete for class sessions, readings,
  deadlines and materials (save upserts on title and course)
- inputs mirror Fillable and Rules; enums, lengths and date formats in
  the field descriptions; no seed, me or dry_run tool
- materials accept meeting_id (absent keeps, null clears, must exist)
  and the managed material JSON shows it
- README: MCP tools section
2026-10-28 14:41:30 +01:00
b08d40cba2 test: expect the BM management MCP tools
- 28 typed management tools pinned in TestBMMCP
- body keys are Fillable columns, create inputs require the required
  columns, material save/update require body, source and url
- no seed or me tool, deletes only where the route deletes
- every description names its scope and permission
2026-10-28 14:39:07 +01:00
df42e30034 feat: MCP view of BM answers (absolute links, no material body by default)
- jz.bm.mcp-links on the token twins and the management API: under MCP,
  url and *_url strings starting with a single / get the app base
- material answers under MCP leave the body out unless include_body,
  marking it with body_omitted; write answers never echo it
- jz.bm.manage keys a dispatched call by user
- bm_materials and bm_material student tools
Plain HTTP answers are unchanged.
2026-10-28 14:37:10 +01:00
464c74b80f test: expect the MCP view of BM answers
- viaMCP, includeBody and the material views (no body unless asked)
- jz.bm.mcp-links: absolute url / *_url under MCP only
- jz.bm.manage keyed per user for dispatched calls
- bm_material and bm_materials in the pinned tool list
Compile-only stubs; the tests fail on their assertions.
2026-10-28 14:34:58 +01:00
e106b36a9a feat: add the BM student MCP tools
- token twins of the student API under /_bm/api/v1/token (user.api_token,
  per-user jz.bm.token bucket, a scope on each), from one mountStudent
- pact.HasMCP with the eleven student tools over the twins
- TestBMMCP and TestTokenMirrorRoutes
2026-10-28 14:32:03 +01:00
65a9a683af build: rebuild the student app (connect page) 2026-10-28 14:09:42 +01:00
72043ac85e feat: reserve the MCP paths
/mcp (the MCP endpoint) and /.well-known (the OAuth metadata documents)
join reservedPrefixes, so they answer a plain 404 here instead of the
student app; /connect stays a client route.
2026-10-28 14:09:22 +01:00