Commit Graph

110 Commits

Author SHA1 Message Date
logaritmisk 66d0624279 test(web): MSW harness with typed handlers, fixtures, and client tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:35:55 +02:00
logaritmisk dcfddc88c7 feat(web): generated OpenAPI types + typed openapi-fetch client with 401 redirect
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:25:10 +02:00
logaritmisk 5267f05089 fix(web): restore shadcn theme tokens in index.css; tidy deps + eslint rule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:21:58 +02:00
logaritmisk b7ec4b1041 feat(web): Tailwind 4 + shadcn/ui + ESLint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:14:12 +02:00
logaritmisk 8466ed4d08 chore(web): drop dangling favicon link (runtime 404)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:10:29 +02:00
logaritmisk f64688a16f feat(web): scaffold Vite + React + TS SPA with Vitest
Bootstraps the web/ SPA: Vite 6 + React 19 + TypeScript 5.8, Vitest
with jsdom, @testing-library/react, and a green smoke test asserting
the App renders its Collection heading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:06:03 +02:00
logaritmisk a177b02145 docs(plan): frontend SPA milestone 1 — task-by-task implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 21:56:54 +02:00
logaritmisk 31e2a3f30a docs(spec): frontend SPA milestone 1 (foundation slice) design
Decomposes the admin SPA into milestones; specs M1 — web/ scaffold,
Vite+React+TS+pnpm+shadcn/ui, openapi-typescript+openapi-fetch typed
client, TanStack Query, react-i18next (sv/en), two-pane master-detail
layout, login/session guard, read-only Objects browse, Vitest+RTL+MSW
tests, memory-serve embed behind a feature gate, 150KB bundle budget.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 21:50:30 +02:00
logaritmisk 8cfcf07387 fix(db): publish gate fires only on transition into public, not re-set
Preserves the documented set-to-current idempotent no-op: re-setting an
already-public object's visibility no longer rejects when a required field
was introduced after publish. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:40:10 +02:00
logaritmisk e96f74f47a feat(db): enforce required-field completeness on publish (#16)
set_visibility now gates the transition to Public: every field definition
with required=true must have a value on the object (typed inventory-minimum
columns are already NOT NULL, so only flexible required fields are checked).
Missing values yield VisibilityError::MissingRequiredFields(keys); the admin
publish endpoint maps it to 422. The gate runs in db so every caller is
protected and the check is atomic with the transition.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:36:24 +02:00
logaritmisk 4921c73fa7 style(api): import reindex into scope rather than crate::-qualify
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:29:50 +02:00
logaritmisk d15afda9b2 feat(api): on-write search reindex after catalogue writes (#17)
Wire best-effort Meilisearch index sync into the admin write paths
(create/update/delete/set_fields/set_visibility). Adds
SearchClient::sync_object (reindex if the object exists, remove if gone —
one uniform path), an optional AppState.search client, and a reindex
helper that logs failures via tracing without failing the committed
write. Server gains MEILI_URL/MEILI_MASTER_KEY/MEILI_INDEX config;
search stays disabled (no-op) when unset. reindex_all remains the
recovery path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:25:43 +02:00
logaritmisk c4e0c4c834 style(api): merge use decl; assert status + breathing room in authority test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 22:39:36 +02:00
logaritmisk 01abd5cbbc feat(api): admin authority management (create + list by kind)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:33:44 +02:00
logaritmisk d81b069b8f style(api): merge use decl; breathing-room blank in vocab test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 22:29:51 +02:00
logaritmisk 7a18e0e9bf feat(api): admin vocabulary + term management
GET/POST /api/admin/vocabularies and GET/POST /api/admin/vocabularies/{id}/terms;
reads gated on ViewInternal, writes on EditCatalogue; labels round-trip verified.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:20:47 +02:00
logaritmisk 8b929c7180 refactor(api): descriptive closure params; exhaustive FieldError match; field-endpoint auth tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:16:50 +02:00
logaritmisk b6a30c3995 feat(api): admin set flexible fields + field-definition listing
- GET /api/admin/field-definitions (ViewInternal) — lists all registered
  field definitions with key, data_type, vocabulary_id, authority_kind,
  required, group, and localized labels
- PUT /api/admin/objects/{id}/fields (EditCatalogue) — replaces an
  object's flexible-field values with replace semantics; validates every
  key against the registry (UnknownField → 422, TypeMismatch → 422,
  Unresolved → 422, ObjectNotFound → 404, Db → 500)
- FieldDefinitionView DTO added; both handlers registered in OpenAPI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:09:43 +02:00
logaritmisk 34e5754815 refactor(api): read object visibility inside update tx; breathing-room nits
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:05:54 +02:00
logaritmisk 3f4da46b78 feat(api): admin object create/update/delete (EditCatalogue, audited as user)
POST /api/admin/objects (draft|internal only; public rejected 422),
PUT /api/admin/objects/{id} (preserves visibility; 204/404),
DELETE /api/admin/objects/{id} (204/404). Every write records
AuditActor::User(<session-user-uuid>). Tests: lifecycle, public-rejection,
unauthenticated-rejection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 21:59:14 +02:00
logaritmisk 1888e185f7 refactor(api): share Pagination across admin/public; cover get-by-id auth
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 21:53:21 +02:00
logaritmisk 0055616099 feat(api): admin object read surface (paginated list + get, ViewInternal)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 21:45:53 +02:00
logaritmisk 3dc621b6dd docs(plans): admin CRUD — object lifecycle + vocab/authority management 2026-06-02 19:02:47 +02:00
logaritmisk 807ac1a9f8 chore: sync Cargo.lock with auth dependencies 2026-06-02 15:21:03 +02:00
logaritmisk 5cfee93037 merge: authentication (email/password) — sessions, extractors, admin surface, CLI bootstrap 2026-06-02 15:20:36 +02:00
logaritmisk 369eee4098 fix(server): --session-cookie-secure flag; scope+char-count password; invalid-email test 2026-06-02 15:16:46 +02:00
logaritmisk dbff95c2a9 feat(server): create-user CLI + session-store migration on startup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 15:07:58 +02:00
logaritmisk 642f709bbe fix(api): drop redundant dev-deps; fix server AppState for cookie_secure; add logout + illegal-transition tests 2026-06-02 15:04:07 +02:00
logaritmisk 5135aeee6c feat(api): admin auth surface (login/logout/me/users/publish) on tower-sessions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:54:03 +02:00
logaritmisk 4e7288731a harden(auth): distinguish session-store failure (500) from absent session (401); exhaustive marker + verify_dummy tests 2026-06-02 14:48:40 +02:00
logaritmisk 992526ef77 feat(auth): argon2id hashing + AuthUser/Authorized<Cap> session extractors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:45:13 +02:00
logaritmisk bea9b6b39a harden(db): case-insensitive email unique index + dup-email test; list_users pagination TODO; from_db note 2026-06-02 14:42:04 +02:00
logaritmisk f8ec2d7cf1 feat(db): users table + repository (create/by_id/by_email/list), audited
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:37:43 +02:00
logaritmisk 9597a42eeb fix(domain): make Editor capability policy fail-closed (exhaustive match) 2026-06-02 14:32:13 +02:00
logaritmisk 74b2cf65ed feat(domain): user identity (UserId, Email), Role/Capability policy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:29:04 +02:00
logaritmisk 1ed9798a1f docs(plans): authentication (email/password) — sessions, extractors, CLI bootstrap 2026-06-02 14:26:19 +02:00
logaritmisk 6cd01f9b97 merge: publishing — visibility transitions, PublicView & public read API 2026-06-02 14:04:32 +02:00
logaritmisk 1b48f082ee chore: sync Cargo.lock after dropping api's uuid dep 2026-06-02 14:04:32 +02:00
logaritmisk 720c7ddbbf chore(api): drop unused uuid dep + redundant domain dev-dep; test internal exclusion + note list/count race 2026-06-02 13:55:01 +02:00
logaritmisk 3c4ada202f feat(api): public read API (PublicView projection, paginated list + get, OpenAPI) 2026-06-02 13:48:17 +02:00
logaritmisk b948cae269 refactor(db): share update path so set_visibility avoids a redundant fetch; tie public-visibility const to the enum; test internal exclusion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:35:36 +02:00
logaritmisk 14cdd2a04a feat(db): audited stepwise set_visibility + public-only object readers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:24:29 +02:00
logaritmisk 5e2ebbc8d9 test(domain): assert IllegalTransition Display message 2026-06-02 13:14:37 +02:00
logaritmisk 59400062ae feat(domain): stepwise Visibility state machine (transition_to + IllegalTransition)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 13:11:01 +02:00
logaritmisk 5ea1febb91 docs(plans): publishing — visibility transitions, PublicView, public read API 2026-06-02 12:50:31 +02:00
logaritmisk f0e00fba40 merge: search (Meilisearch full-text over catalogue objects) 2026-06-02 12:35:37 +02:00
logaritmisk fac4b703ff docs(search): document eventual-consistency model; drop stale Task 2 note 2026-06-02 12:15:18 +02:00
logaritmisk 4bafac397a docs(search): note why reindex test references db crate migrations 2026-06-02 12:12:12 +02:00
logaritmisk 7b91989411 feat(search): build documents resolving term/authority labels; reindex_all
Implements build_document in the search crate: resolves Term and Authority
flexible-field values to their human-readable labels so reindex_all produces
documents that Meilisearch can match on label text, not raw UUIDs.
Adds integration test covering the full reindex→search round-trip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 12:08:07 +02:00
logaritmisk b8d198f150 fix(search): surface failed Meilisearch tasks; make ensure_index idempotent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 11:50:58 +02:00