Glossary Backend Module
Glossary Backend Module
Go backend module handling glossary CRUD operations, SQLite persistence via Ent ORM, reciprocal relationship management, FTS5 search indexing, and background term scanning.
Responsibilities
- HTTP routing and request validation for 6 glossary API endpoints
- SQLite repository implementation (CRUD, slug generation, index text computation)
- Reciprocal relationship synchronization across 6 relationship types
- FTS5 search indexing and removal
- Background term scan job processing (apply/remove ProseMirror marks)
- Usage count tracking (set by term scan, decremented on content deletion)
Public API Surface
| Export | Kind | Description |
|---|---|---|
| GlossaryHandler | struct | HTTP handler with route dispatcher |
| Store (glossary methods) | methods | SQLite repository CRUD implementation |
| TermScanHandler | struct | Background job handler for term scanning |
Internal Structure
The module is organized across three source files in different Go packages, unified by the module's shared purpose:
- handlers/glossary.go -- HTTP handler layer. Dispatches by URL path and HTTP method, validates request bodies, calls the repository store, and submits background term scan jobs. Lives in
backend/internal/http/handlers/. - sqlite/glossary.go -- SQLite repository layer. Implements CRUD via Ent ORM, manages slug generation, reciprocal relationships, FTS5 indexing, and DTO conversion. Lives in
backend/internal/repository/sqlite/. - glossary/term_scan_handler.go -- Background job handler. Applies or removes
glossaryLinkProseMirror marks in content documents. Decoupled from the repository via callback functions. Lives inbackend/internal/glossary/.
Route registration happens in backend/internal/http/server.go, where GlossaryHandler.Glossary is mounted on the /api/projects/{slug}/glossary path prefix. Handler construction and callback wiring occur in backend/cmd/server/main.go (lines 159-206).
Dependencies
| Dependency | Internal / External | Purpose |
|---|---|---|
| data-models/GlossaryEntry | internal | Ent ORM schema for glossary entries (37 fields) |
| data-models/Glossary | internal | Parent entity schema (1:1 with Project) |
repository.ProjectStore |
internal | Interface for store operations (CRUD, listing) |
search.SearchProvider |
internal | FTS5 indexing (optional, may be nil) |
jobs.JobSubmitter |
internal | Background job submission (optional, may be nil) |
prosemirror package |
internal | Mark manipulation for term scanning |
scope package |
internal | Content scope filtering (ItemAppliesToContent) |
Dataflow Diagrams
Security
No authentication or authorization is enforced at the glossary level -- all operations are scoped to the local per-project SQLite database file. The per-project isolation means one project's glossary cannot access another's data.
Input validation:
- Create: non-empty
termandshortDefinitionrequired - Update: at least one field must be non-nil
- Duplicate terms are rejected case-insensitively via
TermEqualFoldcheck
The
image_url field accepts arbitrary URLs. If this is ever rendered as <img src>, it could be an XSS vector. Currently it is only stored, not rendered in the UI.Performance
- Tag filtering is O(n) post-query -- SQLite JSON fields do not support efficient array-contains queries. Tag matching runs in Go after the database query returns results. Mitigated by the 100-entry limit per page.
- Reciprocal sync is O(k) per relationship type -- for each added or removed ID, loads and saves the target entry. With 6 relationship types and small relationship arrays, this is typically fewer than 12 DB round-trips per save.
- Term scan is O(n * m) -- scans n content documents for m terms/aliases per entry. Runs in a background job to avoid blocking the API response.
docChanged()uses JSON serialization comparison to skip unchanged documents. cleanupRelationshipReferences()on delete is O(all entries) -- queries every entry in the glossary to remove the deleted ID from relationship arrays. Acceptable for glossaries under ~10K entries.