Commit graph

8 commits

Author SHA1 Message Date
b382eb857b v0.2.1: top-bar avatar, You/Admin settings groups, admin models
- Replace the gear/username button with a round initials avatar (per-user
  deterministic color via a new daybook_extras template filter); dropdown
  now shows the username and keeps Settings/About/theme/logout. Sets up an
  optional profile photo later.
- Regroup the settings nav into "You" (Account, API Keys, Storage, Demo
  Data) and a superuser-only "Admin" (Users), About separate — the split
  now reflects who each setting affects, not a flat list.
- Register ApiKey (key read-only) and TagColor in Django admin so /admin/
  is complete as the operator break-glass tool, distinct from the themed
  Settings product surface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 11:33:23 +01:00
19c1798f90 Release v0.2.0: multiple named API keys per user
Replaces the single per-user DRF token with named, individually-revocable
API keys, so you can run one key per agent and cut off a leaked one without
disrupting the others.

- New ApiKey model (user, name, dbk_-prefixed key, created_at, last_used_at).
  Data migration copies every existing authtoken 1:1 into a "default" named
  key with the same value, so no client (incl. the sibling test project's
  hardcoded test-user key) needs reconfiguring.
- Auth (resolve_token_user) resolves against ApiKey and stamps last_used_at,
  throttled to one write/minute.
- Settings -> API Keys: table with name/key/created/last-used + per-row
  revoke and a create form; keys viewable on demand. Settings -> Account now
  links here; Settings -> Users issues a named initial key.
- Gated provisioning API for test installations: GET/POST /api/keys and
  DELETE /api/keys/{id}, controlled by DAYBOOK_KEY_PROVISIONING_API (unset ->
  follows DEBUG, so off in deployment). Advertised via key_provisioning_api
  in /api/capabilities; 403 when disabled.
- Docs (api/deployment/quickstart/posting-helper), SKILL.md, and env.example
  updated from "token / Settings -> Account" to "key / Settings -> API Keys".
- Tests: new API + UI suites for keys; existing token-based tests moved to
  ApiKey. Full suite 129 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-04 10:52:43 +01:00
5c46f2aa67 Release v0.1.1: fix collectstatic crash-loop; ship /api/skill
Fixes the deploy blocker found deploying v0.1.0 to cloud-vm-1: the vendored
gridjs.umd.js ended with a //# sourceMappingURL=gridjs.umd.js.map trailer,
but that .map isn't in the repo. The strict CompressedManifestStaticFilesStorage
backend follows the reference and raises MissingFileError, aborting the
entrypoint's collectstatic so the container never served. Stripped the
debug-only trailer (no runtime effect); collectstatic now succeeds from a
pristine checkout (169 copied, 481 post-processed).

Also rolls up this release's already-committed work: GET /api/skill and the
location-agnostic posting-skill rewrite.

- Bump __version__ to 0.1.1.
- Runbook: note that a post-build crash-loop is most likely a collectstatic
  MissingFileError (check docker logs), and how to clear on-server tracked
  drift before checking out a tag.
- CHANGELOG: 0.1.1 entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 16:13:19 +01:00
16737f3ff4 Add version (0.1.0) and a Settings -> About page
Single source of truth: __version__ in daybook_project/__init__.py, starting
at 0.1.0. Surfaced in two places from that constant:
- Settings -> About page (logo, name, version, description), in the settings
  nav and linked from the gear menu.
- GET /api/capabilities now includes "version", so the deploy agent / tester
  can confirm which version is actually running.

Release flow: bump __version__, commit, `git tag vX.Y.Z`, push tag, deploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 14:41:02 +01:00
5f872b4b32 Fix #015 (nested-JSON 500) and #016 (ragged CSV data loss)
#015 — deeply nested JSON raised RecursionError (a RuntimeError, not
ValueError) and became an unhandled 500 on two paths:
- json-kind content validation now catches RecursionError -> 400.
- Added a custom DRF EXCEPTION_HANDLER
  (artifacts/exception_handlers.py, wired via REST_FRAMEWORK) mapping
  otherwise-unhandled RecursionError -> 400, covering the parser-level
  crash on deeply nested request bodies (metadata etc.) too.

#016 — wrap_csv_viewer handed Grid.js rows without reconciling width to the
header, so over-long rows silently dropped cells. Now normalizes every row
to the max width: long rows widen the columns (auto-named "Column N"),
short rows pad with "". No cell is dropped.

Both verified live; 4 regression tests added. 106 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 10:57:07 +01:00
d1b6a19eb3 Fix concurrent posts racing past quota (issue #010)
make_room_for() read usage before any write lock was held, so concurrent
requests could each read stale data and both conclude "this fits" -
confirmed reproducible (measured 616 bytes over a 1551-byte quota with 5
truly concurrent posts).

Fix: force SQLite to acquire its write lock immediately via a no-op UPDATE
on the user's UserProfile row, before reading `used`, wrapped in
transaction.atomic() through the subsequent Artifact.objects.create() so
the lock is held across the whole eviction+insert sequence. Added a 20s
DB timeout as headroom for legitimate contention.

Deliberately no retry/backoff logic - this project's actual load (a
low-volume self-hosted tool) doesn't need it, and SQLite has no row-level
locking regardless, so quota-affecting writes serialize globally either
way. Did fix a real bug found while verifying this: exception handling
around artifact creation only caught QuotaExceeded, so a genuine lock
timeout left the just-written file orphaned on disk - both call sites now
clean up on any exception.

Verified live against the real dev instance (twice, with 5 truly
simultaneous posts against a tight quota) rather than via an automated
test - Django's in-memory shared-cache test database uses a different,
non-retryable SQLite locking model that fails for reasons unrelated to
this fix's correctness.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 15:22:37 +01:00
d458cd3673 Fix CSRF failure behind a TLS-terminating reverse proxy
Django had no CSRF_TRUSTED_ORIGINS and no SECURE_PROXY_SSL_HEADER, so
it always treated requests as plain HTTP. Login over HTTPS behind
Apache/Nginx failed CSRF verification as a result.
2026-09-02 12:17:16 +01:00
940a703cd7 Initial public commit
Daybook: a self-hosted, multi-user Django + DRF app where agents, scripts,
or people publish artifacts and each logged-in user browses their own
time-ordered, label-filterable feed.

This squashes prior local development history (kept privately on a separate
branch) into a single clean starting point for the public repository.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 21:55:02 +01:00