docs/CHANGELOG.md
Rama main · réplica de lectura del repositorio original.
# Changelog
All notable changes to ActaLog will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.3.8] - 2026-10-02 — Health probe fix; Go 1.26 and security updates
### Fixed
- **The container readiness probe could never succeed, and it killed the healthy
app when it failed — crash-looping both deployments for 9 days.**
`docker/scripts/entrypoint.sh` polled `http://localhost:8080/health`. Two defects:
- **`localhost` vs IPv4.** On hosts where `localhost` resolves to `::1` first,
wget connects to `[::1]` while the server binds IPv4 only (`SERVER_HOST`
defaults to `0.0.0.0` and is commonly set to `127.0.0.1`), so the probe was
refused every time. Verified against a live, working instance:
`localhost:8081` → `Connection refused`, `127.0.0.1:8081` → `{"status":"healthy"}`.
- **Hardcoded port 8080.** `SERVER_PORT` was ignored, so a beta instance on 8081
probed the *production* instance on 8080 instead of itself.
Both now use `http://127.0.0.1:${SERVER_PORT:-8080}/health`. The same two fixes
are applied to the Dockerfile `HEALTHCHECK` (kept in shell form so `${SERVER_PORT}`
expands at runtime).
- **The readiness probe no longer kills the application.** On timeout the entrypoint
now logs a warning, skips the seed import (which drives the HTTP API and cannot
work while the app is unreachable), and hands off to `wait $APP_PID`. Previously
it ran `kill $APP_PID; exit 1`, which combined with `restart: unless-stopped` turned
any probe defect into a permanent crash-loop — 25,946 restarts on prod and 26,413
on beta, with zero successful readiness checks, starting at the 2026-07-20 deploy.
This poll only ever gated the optional seed import; it was never a liveness
supervisor. A genuinely dead process is still caught by the in-loop `kill -0`
check (exit 1), and Docker's `HEALTHCHECK` remains the health signal.
### Operations
- Deploy-time verification should probe from **inside** the container
(`docker exec <c> wget -q -O- http://127.0.0.1:$SERVER_PORT/health`), not with
`curl` from the host. Host-side `curl` resolves `localhost` to `127.0.0.1` and
reported healthy throughout, which is why this stayed hidden for 9 days.
### Security
- **Go 1.26.8** (was 1.25.12, now out of support), in `go.mod` and the Docker
builder image. `govulncheck` was failing CI on reachable standard-library
vulnerabilities (`crypto/tls`, `html/template`, `encoding/asn1`) and on
`golang.org/x/text`; it now reports none.
- Web dependencies: `npm audit` went from 14 vulnerabilities (9 high) to 0.
sharp 0.35.4 (libvips CVEs), vitest 4.1.11 (GHSA-82fw-gwwq-j7x9), and the
exact axios pin moved from 1.18.1 to 1.20.0 (prototype-pollution advisory).
- Runtime base image moved to the current Alpine 3.24.2 digest: OpenSSL 3.5.8
(CVE-2026-14456, high, denial of service). Trivy image scan: 0 high/critical.
### Dependencies
- All open Dependabot updates, with `go.sum` and `package-lock.json` regenerated
once: pgx 5.11.0, golang.org/x/crypto 0.57.0, go-sqlite3 1.14.52, mysql driver
1.10.1, chi 5.3.2; the web dev-dependencies group, the Vue ecosystem group
(including pinia 4), dompurify 3.4.12, marked 18.0.6, vuetify 4.1.5; and the
actions/checkout, actions/setup-python and lychee-action updates.
## [1.3.7] - 2026-07-19 — Notes field fix + faster CI
### Fixed
- **Log Workout "Notes" fields: the field label overlapped the placeholder hint.**
The per-movement and per-WOD Notes textareas set both a floating `label="Notes"`
and a `placeholder`; in the compact solo textarea (Vuetify 4) the floated label
rendered on top of the placeholder text. Dropped the redundant label; the field
now shows a single "Notes — how did this feel?" placeholder.
### CI
- Build the release image for **linux/amd64 only** (dropped the arm64 QEMU leg,
which pushed builds to ~20min); no arm64 deploy target exists.
## [1.3.6] - 2026-07-19 — Vuetify 4 UI polish (readability & spacing)
Follow-up readability/spacing fixes on the v1.3.5 Vuetify 4 build, all found while
testing on beta. All are global (theme-token based) so they apply everywhere.
### Fixed
- **Entry-field text was too small to read while typing.** Vuetify 4 renders the
actual `.v-field__input` at ~14px (smaller under `density="compact"`, of which
the app has 342 across 54 files); setting font-size on `.v-field` never reached
the input. Pinned `.v-field .v-field__input` to 16px (also avoids iOS tap-zoom).
- **Low-emphasis text was too light to read.** `text-medium-emphasis` rendered at
0.6 alpha (~#6b6b6b); darkened to 0.82 app-wide.
- **Card count chips were nearly invisible.** `<v-chip color="#e0e0e0">` sets the
*text* to near-white under v4's tonal variant; replaced with `variant="tonal"`
(readable on-surface text) in WorkoutsView, WODsView, WorkoutTimelineView.
- **Text buttons had no horizontal padding.** Labels sat flush to the button
edges; restored Vuetify's per-size inline padding (icon buttons stay square).
- **Tooltips were cramped;** bumped padding from 5px/16px to 8px/14px.
## [1.3.5] - 2026-07-19 — Vuetify 4 migration completed + UI refresh
Completes the Vuetify 3 → 4 migration that v1.3.3's dependency sweep began but
never finished (the un-migrated framework bump silently broke component styling
app-wide; production was interim-pinned to v1.3.0). The app now renders
correctly on Vuetify 4 and ships an approved visual refresh.
### Fixed — Vuetify 4 rendering (the v1.3.3/v1.3.4 regression)
- **`web/src/assets/vuetify3-compat.css`** restores the Material Design 2 look on
Vuetify 4: solo-field elevation, MD2 type scale, container centering/width,
list-item/chip padding, font-weight utilities (`font-weight-bold` was rendering
at 400 app-wide), and app-bar edge padding. Computed styles match v1.3.0 to
sub-pixel across sampled components.
- **`web/src/assets/vuetify3-spacing.css`** re-asserts the 4px-base spacing
utilities (`pa-6`, `mb-4`, …), which Vuetify 4 ships inside `@layer` where they
lost to unlayered rules and silently collapsed padding everywhere.
### Added — approved UI refresh (design layers)
- **`redesign.css`**: 12px card corners with hairline border + soft shadow,
bordered inputs with teal focus ring, outlined toggle chips with filled
selected state, brand-teal bottom-nav active pill (fixed-size, centered, FAB
excluded), list hover feedback, visible keyboard focus.
- **`redesign-plus.css`**: firmer 600-weight titles with tighter tracking,
tabular stat numerals, legible bottom-nav labels, brand-teal FAB, token-based
gradient CTAs, hover lift on interactive cards, app-wide bolder icon strokes.
- Light theme's `background` token is now `#f4f6f9` (off-white ground) so white
surfaces read as cards; all seven themes (light/dark/brutalist/ocean/sunset/
sunrise/myst) verified — every accent is theme-token based.
- Typography changes are family-agnostic: the user font picker (incl.
OpenDyslexic / Atkinson Hyperlegible) is untouched.
### Removed
- Dead CSP-blocked jsdelivr `@mdi/font` CDN `<link>` (icons are bundled locally);
this also clears the console CSP error on every page.
### Security — Go stdlib patch
- Go `1.25.11 → 1.25.12` for **GO-2026-5856** (crypto/tls ECH privacy leak,
reachable) and **CVE-2026-39822** (os.Root symlink traversal) — both flagged by
the govulncheck/Trivy gates, which block releases until patched.
### Docs
- `docs/plans/vuetify-4-migration-plan.md`: scope, root-cause analysis, and the
executed fast path.
## [1.3.4] - 2026-07-04 — CI & supply-chain hardening
Build/CI hardening release. No application behavior changes — no runtime code was
touched.
### Security — Go stdlib patch (caught by the new gate)
- Bumped the Go directive `1.25.0 → 1.25.11` to pick up the fix for **GO-2026-5039** (unescaped input in `net/textproto` error strings), which the new `govulncheck` gate flagged as reachable from the SMTP, import, and admin-user code paths. This finding was previously invisible because there was no vulnerability scanning in CI.
### Added — CI security gates
- **`govulncheck`** (Go, pinned `v1.1.4`) runs on every push/PR and **fails** the build on known vulnerabilities in Go dependencies or reachable code.
- **`npm audit --audit-level=high`** on the web build **fails** on high/critical advisories in frontend dependencies.
- **Trivy image scan** (pinned by digest) in `docker-build.yml`: builds a single-arch image, scans it, and **blocks the push** on fixable HIGH/CRITICAL CVEs (`--ignore-unfixed`) before anything reaches ghcr.io.
### Fixed — golangci-lint config was silently broken
- `.golangci.yml` declared `version: "2"` but still used v1 keys (`linters-settings`, `issues.exclude-rules`), so `golangci-lint config verify` failed — and the failure was hidden by `continue-on-error`. Migrated to valid v2 schema (`linters.settings`, `linters.exclusions.rules`); the exclusion rules now actually apply (findings dropped from ~1,800 to ~605).
### Changed — Lint is now enforcing
- `golangci-lint` is no longer advisory (`continue-on-error` removed). It runs with **`only-new-issues: true`**, so it fails on lint regressions introduced by a change while grandfathering the ~605 pre-existing findings (tracked in `docs/MATURITY_ASSESSMENT.md`).
### Security — Supply-chain pinning
- **All GitHub Actions pinned to commit SHAs** (with a version comment) across every workflow, so a moved/compromised tag can't silently change CI behavior. Dependabot continues to update them.
- **Docker base images pinned by digest** — `node:24-alpine`, `golang:1.25-alpine`, and the `alpine` runtime now reference immutable `@sha256:…` digests instead of floating tags.
### Docs
- Refreshed `docs/MATURITY_ASSESSMENT.md` with a v1.3.3 remediation-status section (base assessment was v1.2.1).
- Updated `docs/ROADMAP.md` current version and recent-release summaries.
## [1.3.3] - 2026-07-04 — Maintenance: dependency & toolchain updates
Maintenance release. No application behavior changes — dependency, toolchain, and
CI hardening only. All 23 changes since v1.3.2 are dependency or build-tooling
bumps.
### Changed — Backend (Go) dependencies
- `golang.org/x/crypto` 0.50.0 → 0.53.0
- `github.com/mattn/go-sqlite3` 1.14.42 → 1.14.47
- `github.com/jackc/pgx/v5` 5.9.2 → 5.10.0
- `github.com/go-chi/chi/v5` 5.2.5 → 5.3.0
- `github.com/go-sql-driver/mysql` 1.9.3 → 1.10.0
### Changed — Frontend (web) dependencies
- `vite` 7.3.1 → 8.0.11
- `vuetify` (bump)
- `axios` 1.13.5 → 1.18.1
- `marked` 17.0.6 → 18.0.5
- `jsdom` 28.1.0 → 29.1.1
- `dompurify` 3.3.3 → 3.4.2
- `sharp` (bump)
- `eslint` 9.39.2 → 10.6.0, `prettier` 3.8.3 → 3.9.3 (dev)
### Changed — Toolchain & CI
- Upgraded the web build to **Node 24** and pinned the npm toolchain (`packageManager: npm@11.16.0`)
- Bumped GitHub Actions: `actions/checkout` 6→7, `actions/cache` 5→6, `actions/upload-pages-artifact` 4→5, `actions/github-script` 8→9, `docker/build-push-action` 6→7, `golangci/golangci-lint-action` 6→9
### Fixed — CI lockfile stability
- Pinned npm to **11.16.0** in CI (`ci.yml`) and the Docker frontend build stage so it matches the version that generates `web/package-lock.json`. `setup-node`'s bundled npm floats with the Node 24.x patch line, and a newer npm reshapes the rolldown wasm-fallback `@emnapi` optional deps, which was breaking `npm ci` on dependency-update PRs.
## [1.3.2] - 2026-05-07 — Admin user lifecycle + protected-user break-glass CLI
### Added — Operator break-glass CLI
- **`actalog admin force-edit-protected`** — operator escape hatch for editing protected accounts when admin paths are unavailable. Five fields: `password | email | name | role | account_disabled`. Per-field audit events with operator metadata (USER, hostname, tty, cwd) for forensics
- Identity-field changes (`email | name | role | account_disabled`) briefly drop both L3 protected-user triggers, perform the UPDATE, then unconditionally reinstall + re-run the boot invariant. Reinstall failure panics with a recovery message — partial trigger state is worse than a hard exit
- Password reads from stdin (no shell-history exposure); identity changes additionally require typing `BREAK-GLASS` to proceed
- **Five new audit events**: `protected_user_break_glass_password`, `protected_user_break_glass_email`, `protected_user_break_glass_name`, `protected_user_break_glass_role`, `protected_user_break_glass_account_disabled` — per-field rather than one event with a discriminator, so alert routing keys off the event type alone
### Added — Admin user lifecycle (originally targeted v1.3.1)
- **`POST /api/admin/users`** — admins can create user accounts with email + password + role; new user can sign in immediately
- **`POST /api/admin/users/{id}/password`** — admin sets a specific password directly; bundles lockout-clear + refresh-token revocation in one operation; protected accounts blocked at L1 + L2
- **AdminUserCreateDialog** on the User Management screen — modal with email/password/name/role/email-verified inputs
- **AdminSetPasswordDialog** on the Profile tab Password Management card — sits alongside the existing Force Password Reset button with explanatory copy distinguishing the two
- **`usePasswordInputs`** composable — shared password+confirm state with show/hide toggle, complexity hint, matching validation; used by both new dialogs
- **Three additional audit events**: `admin_user_created`, `admin_password_set` (with prior failed-attempts/lockout state captured for forensics), `admin_user_create_rejected_protected`
### Security
- Break-glass CLI bypass is documented as accepted residual risk in `docs/security/THREAT_MODEL.md`: shell access on the application host already implies DB-credential access; the CLI is the audited pathway for that existing capability
- Protected emails are rejected at create with their own audit event — distinct from the `protected_user_attack_*` family which targets modifications of existing protected rows
- L1 `ProtectedUserGuard` covers the new set-password endpoint automatically (inside the `/users/{id}` sub-router)
- L2 service-layer `ensureNotProtected` defensive check on `SetPassword` matches the pattern used by `UpdateProfile` / `ForcePasswordReset`
- Password complexity policy unchanged (12+ chars, upper, lower, digit) — single source of truth across self-service, admin-set, and break-glass paths
### Documentation
- `docs/security/PROTECTED_USERS.md` — new "Break-glass CLI" subsection covering when to use, what it does, and audit forensics
- `docs/security/PROTECTED_USERS_RECOVERY.md` — points the 3-AM playbook at the CLI as the primary recovery path
- `docs/security/THREAT_MODEL.md` — admin-compromise + shell-access residual-risk rows
- `docs/USER_PERMISSIONS.md` — new endpoints + UI surfaces + operator CLI
---
## [1.3.0] - 2026-05-03 — Admin user-edit screen + protected-user defense-in-depth
### Added
- **Admin User Edit Screen** at `/admin/users/:id/edit` — tabbed view for editing per-user attributes. v1.3.0 ships the **Profile** tab with name/email/birthday/email-verified field editing, optimistic-concurrency via `updated_at`, and a force-password-reset action that sends a reset email and revokes refresh tokens. Future tabs (Affiliations, Subscriptions, Credits, Preferences, Activity) shown as disabled with version chips.
- `PATCH /api/admin/users/{id}` — partial profile update with `updated_at` precondition; returns 409 on stale `updated_at`
- `POST /api/admin/users/{id}/force-password-reset` — sends password-reset email + revokes all refresh tokens
- Edit pencil-icon button in `AdminUsersView` (disabled for protected users)
### Security — Protected User System (defense-in-depth)
A four-layer defense for system-reserved accounts (currently `br8kwall@gmail.com`):
- **L1 — HTTP middleware** (`pkg/middleware/protected_user.go`): refuses non-GET writes under `/api/admin/users/{id}` with structured 403 + `protected_user_attack_http` audit event
- **L2 — Service guard** (`internal/service/admin_user_service.go`): catches in-process callers (cron, internal APIs) that bypass HTTP. Fires `protected_user_attack_service` audit event
- **L3 — Database trigger** (migration 0.35.0, per-dialect SQL for sqlite3/postgres/mysql): `BEFORE UPDATE` blocks identity-field changes only (`email`, `name`, `role`, `account_disabled`); `BEFORE DELETE` blocks unconditionally. Lifecycle writes (password_hash, last_login_at, email_verified, verification_token, ...) pass through so the protected user can register, log in, and rotate their own password — L1 and L2 remain the primary defenses against admin-screen tampering. Mixed identity+lifecycle UPDATEs are still blocked. See `docs/security/PROTECTED_USERS.md` §3.4 for the full contract
- **L4 — Audit log tagging** (`internal/domain/audit_log.go`): single event per attempt, tagged at the rejecting layer
- **Boot-time invariant**: three sub-checks (triggers exist, triggers fire, protected rows present). Fails closed on hard failure; soft-warn on fresh installs
- **`ACTALOG_SKIP_PROTECTED_INVARIANT=true`** env var: degraded-mode escape hatch. `/health` returns 503; admin user-write endpoints return 503; ERROR-level heartbeat every 60s for alerting
- **Admin CLI**: `actalog admin verify-protected-users [--verbose]` (read-only diagnostic) and `actalog admin reapply-protected-migrations --confirm` (idempotent recovery)
- **Recovery scripts**: `scripts/recover/restore-protected-triggers.sh` + per-dialect SQL files for last-resort recovery when the binary itself is unrunnable
### Documentation
- `docs/security/PROTECTED_USERS.md` — master runbook: purpose, threat model, architecture, list management, verification, recovery, audit forensics, degraded mode
- `docs/security/PROTECTED_USERS_RECOVERY.md` — focused 3-AM-incident playbook
- `docs/security/THREAT_MODEL.md` — app-wide threat model linking each section to its implementation
- `docs/superpowers/specs/2026-04-28-admin-user-edit-design.md` and `docs/superpowers/plans/2026-04-28-admin-user-edit-v1.3.0-plan.md` capture the design and implementation history
### CI
- Generator drift check: `go run ./cmd/gen-protected-emails/` + `git diff --exit-code` keeps Go source ↔ frontend JS in sync
- Lockstep check: migration SQL ↔ standalone recovery scripts can't drift
- Doc code-block lint: `bash -n` validates every shell command in security runbooks
- CODEOWNERS: security-critical paths (`pkg/security/`, migrations, security docs, recovery scripts) require explicit review
### Maintenance
- Migration 0.35.0 is additive (no schema changes, just triggers); existing v1.2.x deployments upgrade by applying the migration
- v1.2.x rollback is safe: triggers stay in the database, security stays on even if the binary rolls back
### Carried into next releases
- v1.3.1: Affiliations tab (gym memberships, coach assignments per gym)
- v1.3.2: Subscriptions, Credits & Documents, Preferences, Activity & Audit tabs
---
## [1.2.4] - 2026-04-28 — Security hardening, part two
Closes the two items deferred from v1.2.3 (`docs/plans/SECURITY_HARDENING_PLAN.md` Steps 2 and 5).
### Security
- **Security response headers middleware** — new `pkg/middleware/SecurityHeaders` sets `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Strict-Transport-Security: max-age=31536000; includeSubDomains`, and a project-tuned `Content-Security-Policy` on every response. Wired in `cmd/actalog/main.go` after the CORS middleware so it applies to API, static assets, and `/health`. CSP allows `'unsafe-inline'` for *styles* (Vuetify dynamic theme injection requires it) but keeps scripts strict (`script-src 'self'` only)
- **Avatar upload magic-byte validation** — `internal/handler/user_handler.go` no longer trusts the client-supplied `Content-Type` header. The first 512 bytes of the uploaded file are read and passed to `http.DetectContentType` (WHATWG MIME Sniffing); the file pointer is then rewound for the subsequent copy. A filename-extension allowlist (`.jpg/.jpeg/.png/.gif/.webp`) closes the residual stored-XSS path where image magic bytes could be uploaded under a `.html` filename and later served with a `text/html` content-type by the static file handler
- New negative tests cover the spoofed-Content-Type and disallowed-extension cases
### CI
- **CI Failure Notify workflow now deduplicates and auto-closes.** Previously every CI failure (including each Dependabot rebase) opened a fresh `[CI] Workflow failure: ...` issue; old issues never closed. The workflow now (a) re-uses an existing tracking issue with the same `(workflow, branch)` title instead of spawning duplicates, and (b) on the next *successful* CI run for the same branch, closes the tracking issue with a comment linking the green run. Switched from `peter-evans/create-issue-from-file` to `actions/github-script` to access the issue list/update REST endpoints
### Maintenance
- Eight Dependabot bumps merged: actions/deploy-pages 4→5, docker/setup-buildx-action 3→4, lib/pq 1.12.1→1.12.3, x/crypto 0.49.0→0.50.0, pgx 5.9.1→5.9.2, vue-ecosystem group, go-sqlite3 1.14.38→1.14.42, dev-dependencies group (8 npm updates)
- Two Dependabot PRs closed with policy comments: Vuetify 4.0.5 (held by watch-and-wait policy until 4.1.x), axios 1.14.0 (post supply-chain compromise; close-and-wait until npm reissues clean 1.14.x or 1.15.x ships)
---
## [1.2.3] - 2026-04-03 — Security hardening release
### Security
- **Password policy** — raised minimum from 8 to 12 characters; now requires at least one uppercase letter, one lowercase letter, and one digit. Enforced in registration, password reset, and password change flows (`internal/service/user_service.go`). UI hints and client-side length checks updated in RegisterView, SettingsView, ResetPasswordView, and AdminUserImportExportView
- **CORS allowlist enforcement** — `pkg/middleware/cors.go` previously echoed the request's `Origin` back as `Access-Control-Allow-Origin` for both allowed and disallowed origins, making the `CORS_ORIGINS` allowlist inert. Disallowed origins now receive no `Access-Control-Allow-Origin` header (browser default-deny). Test in `cors_test.go` strengthened to assert header absence
- **Error sanitisation** — `WriteError()` no longer forwards raw internal error strings to HTTP responses; unknown errors return a generic message (`internal/handler/errors.go`)
- **XSS prevention** — DOMPurify 3.3.3 added to `MarkdownRenderer.vue`; all `v-html` output is sanitized before DOM insertion
- **serialize-javascript** — pinned to 7.0.5 via `package.json` overrides, fixing RCE (GHSA-5c6j-r48x-rmvq) and CPU exhaustion DoS (GHSA-qj8w-gfj5-8c6v) in the `vite-plugin-pwa → workbox-build → @rollup/plugin-terser` chain
- **Rate limiter IP extraction** — `X-Forwarded-For` header now correctly takes the leftmost (original client) IP from comma-separated lists; `RemoteAddr` port suffix stripped to prevent per-reconnect bucket bypass
- **Audit logging** — `rate_limit_exceeded` events now emitted via `OnExceeded` callback on both auth and password-reset rate limiters
### Added
- Admin organizations list shows a member count column (`LEFT JOIN user_organizations` in `OrganizationRepository.List`)
- Edit-from-list action in admin organizations view (reuses existing `PUT /api/admin/organizations/{id}`)
### Documentation
- `docs/OWASP_AUDIT_2026-04-03.md` — OWASP Top-10 audit findings
- `docs/MATURITY_ASSESSMENT.md` — Trail of Bits 9-category scorecard
- `docs/plans/SECURITY_HARDENING_PLAN.md` — phased remediation plan (Steps 2 and 5 carried into backlog)
### Maintenance
- golangci-lint upgraded from v1.64.8 to v2.11.4; config migrated to v2 format (`version: "2"`)
- Frontend unit tests added to CI pipeline (`web-test` job in `.github/workflows/ci.yml`); 11 new Vitest suites covering App shell, auth store, axios interceptor, and 8 admin views
### Known gaps (deferred to a future release — tracked in TODO.md)
- Security response headers middleware (CSP, HSTS, X-Frame-Options)
- Avatar upload still trusts client `Content-Type` header rather than detecting magic bytes
---
## [1.2.1] - 2026-04-01
### Added
- Calendar header now displays workout count for the viewed month
### Fixed
- Calendar view now fetches all workouts instead of defaulting to 20 (fixes missing entries for active users)
- Admin breadcrumb link returning 404
- Avatar upload click target too small; removed stale debug logging
### Changed
- Nav bar icons switched to heavier filled MDI variants for improved visual weight
### Security
- Pinned axios to `1.13.5` across frontend to prevent auto-upgrade into compromised versions 1.14.1 / 0.30.4 (supply chain attack via hijacked npm account, 2026-03-31)
- `npm audit fix`: patched brace-expansion (ReDoS), picomatch (method injection / ReDoS), undici (WebSocket memory/smuggling), yaml (stack overflow)
- Updated Go deps: `x/crypto` 0.48→0.49, `pgx/v5` 5.8→5.9, `go-sqlite3` 1.14.34→1.14.38, `lib/pq` 1.11→1.12
### Maintenance
- CI: `setup-go` now uses `go-version-file: go.mod` so toolchain tracks the module automatically
- CI: pinned `golangci-lint-action` to v6 (v9 dropped golangci-lint v1.x support)
- Fixed date-sensitive test `TestUserWorkoutRepository_GetActiveUsersThisMonth` (failed on first day of each month)
- Updated GitHub Actions: `docker/login-action` 3→4, `docker/metadata-action` 5→6, `docker/build-push-action` 6→7, `docker/setup-buildx-action` 3→4, `actions/checkout` 4→6, `github/super-linter` 4→7
- Updated frontend deps: Vue 3.5.28→3.5.31, vue-router 5.0.3→5.0.4, vitest 4.0.18→4.1.0, sass 1.97→1.98, marked 17.0.1→17.0.3
---
## [1.2.0-beta] - 2026-02-19
### Added - Configurable Beta Logo
- **Environment-Variable-Driven Logo Switching**
- New `LOGO_VARIANT` environment variable (`"logo"` default, `"betalogo"` for red beta logo)
- Backend: `LogoVariant` field in `AppConfig`, exposed via `/api/version` response
- Frontend: Auth store fetches variant, computes `logoPath`, dynamically updates favicon and apple-touch-icon
- All three logo locations updated (App.vue header, LoginView, RegisterView)
- Full beta icon asset set generated from `betalogo.svg` (SVG, 12 PNGs, favicon.ico, 6 apple-touch-icons)
- Example `.env` files updated with `LOGO_VARIANT` documentation
### Added - PR Leaderboards & Consistency Achievements
- **Leaderboard search API response parsing fix and header nav icon**
## [1.1.0-beta]
### Added - Coach Role & Role Rename
- **Three-Tier Role System**
- Renamed `"user"` role to `"athlete"` across backend, frontend, and tests
- Added `"coach"` as a new middle-tier role: athlete < coach < admin
- Database migration (0.32.0) renames existing `user` records to `athlete`
- **CoachOrAdmin Middleware**
- New `CoachOrAdmin` middleware in `pkg/middleware/auth.go` for coach-accessible routes
- Coaches bypass subscription checks (same as admins) in `RequireActiveSubscription`
- 14 comprehensive middleware tests covering all role permutations
- **Dedicated Coach API Routes**
- `GET /api/coaches/me/sessions` - Coach's upcoming sessions (admins see all sessions across all gyms)
- `GET /api/coaches/sessions/{id}/roster` - Session roster
- `POST /api/coaches/sessions/{id}/check-in/{rid}` - Check in athlete
- `POST /api/coaches/sessions/{id}/no-show/{rid}` - Mark no-show
- `POST /api/coaches/sessions/{id}/complete` - Complete session
- Organization-level access verification for coach actions
- **Frontend Updates**
- Coach nav button in bottom navigation (visible for coach/admin when scheduling enabled)
- Coach Dashboard updated to use `/api/coaches/` routes instead of admin routes
- Admin Users panel shows three role options: Athlete, Coach, Admin
- Router guard for `requiresCoach` meta on coach routes
- **Admin All-Sessions Visibility**
- Admins see ALL upcoming sessions on Coach Dashboard (not limited by coach assignments)
- New `GetAllUpcoming` repository method and `GetAllUpcomingSessions` service method
### Added - Delete Class with Sessions
- **Enhanced Class Template Deletion**
- Three delete modes via `?mode=` query parameter:
- `template_only` (default): Delete template only, sessions become orphaned
- `with_future_sessions`: Delete template and future sessions, preserve past sessions
- `with_all_sessions`: Delete template and all sessions (past and future)
- Cascade deletion of related data (coaches, reservations, waitlist, notifications)
- Credit refunds for unconfirmed reservations (status != checked_in/attended)
- User notifications for cancelled sessions with session details
- **New UI Delete Dialog**
- Radio button selection for delete mode
- Session counts displayed for each option
- Warning about cascading deletes
- Information about notifications and credit refunds
- **API Enhancement**
- `DELETE /api/admin/scheduling/templates/{id}?mode=<mode>`
- Response includes: `sessions_deleted`, `notifications_sent`, `credits_refunded`
- **Repository Methods**
- `GetSessionIDsByTemplateID`, `GetFutureSessionIDsByTemplateID`, `DeleteByIDs` on ClassSessionRepository
- `DeleteBySessionIDs` on SessionCoachRepository, ReservationRepository, WaitlistRepository
- `GetBySessionIDs` on ReservationRepository
- `RefundCredit` on UserClassCreditsRepository
- **Tested on:** SQLite, MariaDB (192.168.1.234), PostgreSQL (192.168.1.143)
### Added - Class Scheduling System (Phase 4 Complete)
- **Documents Management**
- Gym-specific document types (waivers, liability forms, health forms)
- Track required vs optional documents per organization
- Document expiration support with configurable validity periods
- Admin CRUD for document types
- **User Documents Tracking**
- Per-user document completion status (pending, completed, expired)
- Expiration date tracking for time-limited documents
- User view of pending/completed documents
- Admin can mark documents as completed
- **Class Packages (Credit System)**
- Flexible credit packages (e.g., "10-Class Pack", "Monthly Unlimited")
- Configurable credits per package with validity periods
- Price tracking for package purchases
- Active/inactive package status
- **User Credits**
- Credit balance tracking per user per organization
- Credits automatically populated from package when purchasing
- Expiration tracking for time-limited credit packages
- Available credits calculation excluding expired records
- **Waitlist System**
- Join waitlist when class is full
- Automatic position tracking
- Leave waitlist functionality
- Waitlist status indicators (waiting, promoted, expired, cancelled)
- **Class Notifications**
- Foundation for class reminders and waitlist promotions
- Notification types: reminder, waitlist_promoted, class_cancelled, class_updated
- **Frontend Views**
- `MyCreditsView.vue` - User's credits, documents, and waitlist entries
- `AdminPackagesView.vue` - Admin management with 3 tabs (Packages, Documents, User Credits)
- `ScheduleView.vue` - Added waitlist join/leave buttons and position indicators
- **API Endpoints (Phase 4)**
- `GET/POST/PUT/DELETE /api/admin/gyms/{id}/documents` - Document type management
- `GET/POST/PUT/DELETE /api/admin/gyms/{id}/packages` - Package management
- `POST /api/admin/gyms/{id}/users/{id}/credits` - Add credits to user
- `GET /api/gyms/{id}/users/me/credits` - Get user's credit balance
- `GET /api/gyms/{id}/users/me/documents` - Get user's document status
- `POST/DELETE /api/sessions/{id}/waitlist` - Join/leave waitlist
- `GET /api/users/me/waitlist` - Get user's waitlist entries
- **Database Migration (0.27.0)**
- `documents` - Document types per organization
- `user_documents` - User document completion tracking
- `class_packages` - Credit package definitions
- `user_class_credits` - User credit balances
- `waitlist_entries` - Session waitlist queue
- `class_notifications` - Class-related notifications
### Fixed
- **PurchaseCredits Bug** - Now correctly uses package credits when `package_id` is provided without explicit `credits` value
### Added - Workout Template Instructions Field
- **Instructions Field for Template Movements**
- New `instructions` column on `workout_template_movements` table
- Supports markdown-formatted coaching cues, setup notes, and technique reminders
- Optional field - movements work without instructions
- Preserved through full CRUD lifecycle
- **Instructions Field for Template WODs**
- New `instructions` column on `workout_template_wods` table
- Allows WOD-specific scaling options, standards, and modifications
- Consistent behavior with movement instructions
- **Frontend Updates**
- Instructions textarea in `WorkoutTemplateEditView.vue` for both movements and WODs
- Instructions display in `WorkoutTemplateDetailDialog.vue` with markdown rendering
- Collapsible sections for cleaner UI when instructions are present
- **Backend Updates**
- Domain models updated with Instructions field
- Repository queries updated to include instructions in all operations
- Handler validation for instructions field
- **Comprehensive Test Coverage**
- Backend unit tests for service and handler layers
- Frontend unit tests in `WorkoutTemplateEditView.test.js` (573 lines)
- Integration tests in `workout_template_test.go` covering:
- Full CRUD lifecycle for instructions
- Special characters (markdown, unicode, HTML-like)
- Multiple movements/WODs with different instructions
- Optional field behavior (empty/null handling)
### Fixed
- **Repository Query Bug** - Fixed `GetByIDWithDetails` in `workout_repository.go` to include `instructions` column in SELECT queries for both movements and WODs
## [0.24.0-beta] - 2026-01-14
### Added - Data Quality & Duplicate Detection System
- **Admin Data Quality Dashboard**
- New `AdminDataQualityView.vue` with 3 tabs (Overview, Duplicates, Data Issues)
- Full database scan with summary cards for duplicates and issues
- Duplicates by entity type breakdown with clickable View buttons
- Data quality checks display with icons, descriptions, and counts
- Zero-state display with success checkmarks when no issues found
- **Duplicate Detection Engine**
- Scan 5 entity types: movements, WODs, user_workouts, users, workouts
- Case-insensitive name matching for movements, WODs, workouts
- Case-insensitive email matching for users
- Composite key matching for user_workouts (user_id + date + name)
- Shows duplicate group count and record count per entity type
- FK reference counts displayed for informed merge decisions
- **Duplicate Merge Functionality**
- Preview merge operation showing FK impact before execution
- Safe merge with FK updates in database transaction
- Automatic child record handling (movements/WODs from duplicates)
- Audit logging of all merge operations (`duplicate_merge` event type)
- Per-entity type merge with keep/delete ID selection
- **Data Quality Issue Detection**
- 4 quality check types with severity levels:
- Orphaned FK references (error) - FK pointing to deleted records
- Empty required fields (error/warning) - Missing user names, movement names, WOD names
- Future workout dates (warning) - Workouts scheduled in the future
- Invalid email formats (warning) - Malformed email addresses
- Filter chips for issue type filtering in Data Issues tab
- Clickable cards to navigate from Overview to filtered issue lists
- **API Endpoints**
- `GET /api/admin/data-quality/duplicates` - Scan all entities
- `GET /api/admin/data-quality/duplicates/summary` - Quick summary
- `GET /api/admin/data-quality/duplicates/{entity}` - Scan specific entity
- `POST /api/admin/data-quality/duplicates/merge/preview` - Preview merge
- `POST /api/admin/data-quality/duplicates/merge/confirm` - Execute merge
- `GET /api/admin/data-quality/issues` - Scan for quality issues
- **Demo Mode Configuration**
- Added DEMO MODE section to `.env.example`
- Documented implemented vs planned demo features
- Current workaround instructions for basic demo setup
- Planned settings: DEMO_MODE, DEMO_USERS, DEMO_RESET_ON_STARTUP, etc.
### Added - Audit Logging Enhancements (2026-01-13)
- **Password Change Audit Logging**
- New `password_changed` event logged when users change their password
- Captures user email in event details
- Works for both browser and API password changes
- **Password Reset Audit Logging**
- New `password_reset` event logged when password is reset via token
- Tracks which user completed the password reset
- **User Deletion Audit Logging Fix**
- Fixed `user_deleted` event to properly log admin deletions
- Stores target user info (id, email, name) in details JSON
- Handles FK constraint by using nil target_user_id (user already deleted)
- **Duplicate Audit Log Bug Fix**
- Fixed bug where SQLite/MySQL audit logs were being inserted twice
- Repository Create() was calling both QueryRow and Exec for same INSERT
- Now correctly uses Exec with LastInsertId for non-RETURNING databases
### Added - Admin Metrics Dashboard (2026-01-13)
- **System Metrics Dashboard**
- New `GET /api/admin/metrics` endpoint returning all dashboard metrics
- Real-time statistics for admin users
- Single API call for efficient frontend consumption
- **User Statistics**
- Total users count
- Active users this month (users who logged workouts)
- New users this month
- Disabled users count
- **Workout Statistics**
- Total workouts logged across all users
- Workouts logged this month
- Average workouts per user
- **Content Statistics**
- Total movements (standard + user-created)
- Total WODs (standard + user-created)
- Total workout templates (standard + user-created)
- Breakdown of user-created vs standard content
- **Subscription Statistics**
- Active subscriptions count
- Expiring soon (within 7 days)
- Expired subscriptions count
- **System Health**
- Recent audit events (24h)
- Email success rate
- Total emails sent
- Failed emails count
- **Workout Trends**
- 30-day workout activity chart
- Daily workout counts with Chart.js visualization
- **Frontend Components**
- `StatCard.vue` - Reusable stat card with icon, value, label, subtitle
- `WorkoutTrendsChart.vue` - Bar chart for workout trends
- `AdminMetricsDashboardView.vue` - Main dashboard view
- Updated `AdminView.vue` with System Metrics card
- **Backend Infrastructure**
- Cross-database date helpers for SQLite/PostgreSQL/MySQL
- New repository methods: `CountNewThisMonth()`, `CountDisabled()`, `CountActive()`, `CountExpired()`, `CountExpiringSoon()`, `GetWorkoutTrends()`
- `AdminMetricsService` aggregating all metrics
- `AdminMetricsHandler` for HTTP endpoint
### Added - User Import/Export System (2026-01-13)
- **Bulk User Import from CSV**
- New `POST /api/admin/user-management/import/preview` endpoint for CSV validation
- New `POST /api/admin/user-management/import/confirm` endpoint to execute import
- Two-phase import: preview with validation → confirm to create users
- CSV format: `email,name,password` columns required
- Duplicate email detection with skip option
- Password validation (minimum 12 characters with complexity requirements)
- All imported users get role `user` and permanent free subscription
- Passwords hashed with bcrypt (cost 12)
- **User Export to CSV**
- New `GET /api/admin/user-management/export` endpoint
- Downloads CSV with all user emails and names
- Passwords NOT included for security
- Can be used as template for bulk imports
- **Batch Password Reset**
- New `GET /api/admin/user-management/filter` endpoint for filtered user listing
- New `POST /api/admin/user-management/batch-password-reset` endpoint
- Filter users by name/email search and creation date range
- Select multiple users and send password reset emails in bulk
- Uses existing password reset token system (32-byte tokens, 1-hour expiry)
- Maximum 100 users per batch for safety
- **Frontend Components**
- `AdminUserImportExportView.vue` - Tabbed interface for import/export/reset
- Drag-and-drop CSV upload zone
- Import preview table with validation status per row
- User selection table with filters and pagination
- Updated `AdminView.vue` with User Import/Export navigation card
- New route `/admin/users/import-export`
- **Backend Infrastructure**
- `UserListFilter` type in domain layer for flexible querying
- `ListWithFilter()` and `CountWithFilter()` repository methods
- `UserImportService` with preview/confirm/export/filter/batch-reset methods
- `UserImportHandler` with multipart form handling (max 10MB)
- Automatic subscription creation for imported users
- **Documentation**
- Screenshots for all tabs (import, export, password reset, preview)
- Updated admin README with User Import/Export section
- API endpoint documentation with examples
## [0.22.0-beta] - 2026-01-09
### Added - Benchmark API Endpoint
- **Comprehensive Benchmark Endpoint**
- New `POST /api/benchmark` endpoint for API performance testing
- Exercises database, serialization, and business logic operations
- Uses isolated `benchmark_data` table (no production data affected)
- Auto-cleanup of benchmark data after each run
- **Database Benchmarks** (9 operations)
- Single insert, bulk insert (100 records)
- Select by ID, select by key (index lookup)
- List with pagination, filtered queries
- Update and delete operations
- User-scoped cleanup
- **Serialization Benchmarks** (4 operations)
- JSON marshal/unmarshal for small payloads (1 object)
- JSON marshal/unmarshal for large payloads (100 objects)
- **Business Logic Benchmarks** (5 operations)
- 1RM calculations using prmath package (1000 iterations)
- Intensity calculations (1000 iterations)
- Input validation (100 iterations)
- String operations (1000 iterations)
- Date operations with timezone handling (1000 iterations)
- **Concurrent Benchmarks** (3 operations, optional)
- Parallel reads (10 goroutines)
- Parallel writes (5 goroutines)
- Mixed read/write operations (10 goroutines)
- Enable with `?concurrent=true` query parameter
- **Configurable Record Count**
- New `records` query parameter for `/api/benchmark` endpoint
- Default: 1,000 records, Maximum: 500,000 records
- Example: `POST /api/benchmark?records=10000`
- Scales database operations to stress-test the system
- **Complex Benchmark Data**
- Large text fields (5-10KB random text with paragraphs)
- Nested JSON blobs (5-level deep structures with arrays)
- UUID test keys for uniqueness
- Random numeric values (floats, integers, booleans)
- Realistic data to push serialization and storage
- **Supporting Endpoints**
- `GET /api/benchmark/status` - Quick status check
- `DELETE /api/admin/benchmark/data` - Admin cleanup of all benchmark data
### Added - PWA 1.2.0 Features
- **Improved Update Flow**
- Changed from `autoUpdate` to `prompt` registerType
- User-controlled updates via UpdatePrompt component
- Better state management in PWA store
- **PWA Assets Generator**
- New `pwa-assets.config.js` for automatic icon generation
- Generate all icons from single source image
- New npm script: `npm run generate-pwa-icons`
- **Enhanced Caching**
- Added caching for local fonts (1 year expiration)
- Added caching for uploaded images (7 days, StaleWhileRevalidate)
- Extended glob patterns for font files (woff, ttf)
### Changed
- **Database Migration 0.22.0**
- Added `benchmark_data` table for safe read/write testing
- Indexes on `test_key` and `created_by` columns
- Foreign key to users table with CASCADE delete
- **Server Timeout Defaults**
- Updated default SERVER_READ_TIMEOUT from 15s to 30s
- Updated default SERVER_WRITE_TIMEOUT from 15s to 60s
- Supports long-running benchmark API requests without EOF errors
### Fixed
- **EOF Errors on Long-Running Benchmark Requests**
- Fixed HTTP client receiving EOF when benchmark takes >15 seconds
- Root cause: SERVER_WRITE_TIMEOUT was set too low for large record counts
- Added explicit Content-Length header to avoid chunked encoding issues
- For 100k+ records, recommend SERVER_WRITE_TIMEOUT=120s or higher
## [0.21.0-beta] - 2026-01-09
### Added - Frontend Testing Framework
- **Vitest Testing Setup**
- Installed Vitest with Vue Test Utils, jsdom, and v8 coverage
- Created `vitest.config.js` with Vue/Vuetify support
- Added test setup file with jsdom mocks (ResizeObserver, matchMedia, IntersectionObserver)
- New npm scripts: `test`, `test:run`, `test:coverage`
- **Example Tests**
- `src/utils/timezone.test.js` - 20 tests for timezone utilities
- `src/components/UserAvatar.test.js` - 11 tests for component rendering
### Fixed - CI/CD and Code Quality
- **Linting Issues Resolved**
- Fixed gofmt formatting across all Go files
- Updated `.golangci.yml` with relaxed thresholds for complex query builders
- Added `font_family` column to base database schema for all drivers
### Updated - Dependencies
- **Go Dependencies**
- `github.com/jackc/pgx/v5` 5.7.4 → 5.8.0
- `github.com/mattn/go-sqlite3` 1.14.32 → 1.14.33
- **Frontend Dependencies**
- `vuetify` 3.10.11 → 3.11.6
- `eslint-plugin-vue` 9.33.0 → 10.6.2
- `vite-plugin-pwa` 0.21.2 → 1.2.0
- Updated dev-dependencies group (4 packages)
## [0.20.0-beta] - 2026-01-08
### Changed - UI Visual Refresh
- **Form Input Style Update**
- Changed default form input variant from `outlined` to `solo` (filled background, no border)
- Cleaner, more modern appearance for text fields, selects, textareas, autocomplete, and combobox
- Updated 43 Vue files to remove explicit `variant="outlined"` attributes
- **Card Border Removal**
- Removed global card borders for a cleaner look
- Cards now rely on elevation/shadow and background contrast
- Removed inline border styles from all components
### Added - Myst Grayscale Theme
- **New Theme: Myst**
- Elegant grayscale theme with light-gray background
- Dark text on light backgrounds for readability
- Inverted buttons (dark buttons with white text)
- Professional, colorless aesthetic
- Added to theme selector with fog icon
## [0.19.0-beta] - 2026-01-07
### Added - User-Customizable Fonts
- **10 Font Options for User Preference**
- System Default (device's native font stack)
- Inter, Roboto, Lato, Fira Sans (modern UI fonts)
- Lexend (optimized for reading fluency)
- OpenDyslexic, Atkinson Hyperlegible (accessibility fonts)
- Source Serif Pro (classic serif)
- JetBrains Mono (developer monospace)
- **Self-Hosted Web Fonts**
- All fonts bundled as woff2 files in `web/public/fonts/`
- No external CDN dependencies
- All fonts licensed under SIL OFL 1.1 or Apache 2.0
- **Backend Integration**
- New `font_family` field in user_settings domain model
- Database migration v0.21.0 adds font_family column
- Syncs font preference across devices
- **Frontend Implementation**
- New `font.js` Pinia store for font state management
- CSS variable `--app-font-family` for dynamic switching
- Font selector UI in Settings view
- localStorage caching for instant load on return visits
- Vuetify component overrides for consistent font application
### Added - Markdown Support for Descriptions
- **Description Fields Support Markdown Formatting**
- Movements, WODs, and workout templates now render Markdown
- Added "Format text using Markdown" hint to all description textareas
- MovementDetailView updated to use MarkdownRenderer component
### Added - Admin Notification System for User Events
- **Email Notifications for Administrators**
- Notify admins when users are created, modified, or deleted
- Events: registration, profile updates, role changes, disable/enable/unlock/delete
- Per-admin opt-out setting in user_settings (`admin_user_event_notifications`)
- Email includes before/after values for changes
- Actor (admin who performed action) excluded from notifications
- **In-App Notifications for Administrators**
- Real-time in-app notifications for all admin user events
- Notification types: `admin_user_created`, `admin_user_updated`, `admin_user_role_changed`, etc.
- Links directly to user management in admin panel
- Notifications sent to all admins except the actor
- **New Service: AdminNotificationService**
- Centralized admin notification logic
- Async email sending (non-blocking)
- Graceful degradation when email disabled
- Files: `internal/service/admin_notification_service.go`
- **Database Migration v0.20.0**
- Added `admin_user_event_notifications` column to `user_settings` table
### Added - Expiring/Expired Subscription Management
- **Admin Dashboard Subscription Views**
- List subscriptions expiring within N days (7, 14, 30, 60, 90)
- List all expired (overdue) subscriptions
- Separate views for user and organization subscriptions
- **New API Endpoints**
- `GET /api/admin/subscriptions/users/expiring?days=N` - Expiring user subscriptions
- `GET /api/admin/subscriptions/users/expired` - Expired user subscriptions
- `GET /api/admin/subscriptions/organizations/expiring?days=N` - Expiring org subscriptions
- `GET /api/admin/subscriptions/organizations/expired` - Expired org subscriptions
- **Repository Methods**
- Added `ListExpiring(days int)` and `ListExpired()` to both subscription repositories
- Multi-database support (SQLite, PostgreSQL, MySQL) with database-native date arithmetic
### Added - User Timezone Support
- **Per-User Timezone Settings**
- Users can set their preferred timezone in settings
- Dates displayed in user's local timezone throughout the application
- Database migration v0.19.0 adds `timezone` column to `user_settings`
- **Frontend Integration**
- Timezone selector in Settings view
- Utility functions for timezone-aware date formatting
- Pinia store for timezone state management
### Fixed - PostgreSQL Compatibility
- **GetPersonalRecords Query Fix**
- Added aggregate functions (MAX) to all non-grouped SELECT columns
- Fixes PostgreSQL strict GROUP BY enforcement error
- Scan workout_date as string and parse to time.Time (MAX returns string in SQLite)
- All databases (SQLite, PostgreSQL, MySQL) now work correctly
- **Base Schema Sync**
- Added `admin_user_event_notifications` column to base schema for all database types
- Ensures fresh database installations have all columns
### Fixed - Test Suite Improvements
- **Test Coverage for Subscription Features**
- Repository tests for `ListExpiring` and `ListExpired` methods
- Service tests for all 4 new subscription service methods
- Handler tests with mock implementations
- **Pre-existing Test Fixes**
- Fixed backup handler test case sensitivity ("Filename" → "filename")
- Updated WOD service test for removed source validation constraint
- Integration test now outputs response body on failure for debugging
### Added - Merge/Upsert for Database Restore and Import Duplicate Handling
- **Database Restore Modes**
- Added three restore modes to `RestoreBackup` API:
- `replace` - Delete all data, insert from backup (original behavior)
- `merge` - Update existing records by natural key, insert new ones
- `skip` - Only insert records that don't exist
- Natural key matching for record identification:
- Users matched by email
- Movements matched by name
- WODs matched by name
- Organizations matched by name
- User workouts matched by user_id + workout_date + workout_name
- ID remapping for foreign key references during merge/skip restore
- Returns detailed `RestoreResult` with statistics (records created, updated, skipped)
- **Import Duplicate Handling Enhancements**
- **User Workout Import**: Added `updateDuplicates` parameter
- Duplicate detection using user_id + workout_date + workout_name
- Can now skip OR update existing workouts during import
- **Wodify Import**: Added `skipDuplicates` and `updateDuplicates` parameters
- `skipDuplicates=true`: Skip workouts that already exist for a date
- `updateDuplicates=true`: Replace existing workout data for a date
- Added `WorkoutsSkipped` to import result statistics
- **API Changes**
- `POST /api/admin/backups/{filename}/restore` now accepts `mode` parameter:
```json
{"confirm": true, "mode": "merge"} // or "skip" or "replace"
```
- `POST /api/import/user-workouts/confirm` now accepts `update_duplicates` form field
- `POST /api/import/wodify/confirm` now accepts `skip_duplicates` and `update_duplicates` form fields
**Files Modified:**
- `internal/domain/backup.go` - Added RestoreMode type and RestoreResult struct
- `internal/domain/wodify_import.go` - Added WorkoutsSkipped field
- `internal/service/backup_service.go` - Implemented merge/upsert with ID remapping (646+ lines)
- `internal/service/import_service.go` - Added updateDuplicates to user workout import
- `internal/service/wodify_import_service.go` - Added skip/update duplicate handling
- `internal/handler/backup_handler.go` - Added mode parameter parsing
- `internal/handler/import_handler.go` - Added update_duplicates form field
- `internal/handler/wodify_import_handler.go` - Added duplicate handling form fields
### Fixed - PostgreSQL Schema Migration Support
- **Critical Fix: PostgreSQL Custom Schema Support**
- Fixed `checkTableExists()` to use `CURRENT_SCHEMA()` instead of hardcoded 'public'
- Fixed `checkColumnExists()` to filter by `CURRENT_SCHEMA()` to avoid false positives
- **Impact**: Migrations now work correctly with custom PostgreSQL schemas (e.g., `DB_SCHEMA=actalog`)
- **Root Cause**: Previous code hardcoded `table_schema='public'` which failed for non-public schemas
- **Behavior Before Fix**:
- Migration checks would fail to find existing tables in custom schemas
- Would attempt to re-create tables, causing "table already exists" errors
- Required manual database rebuild when using custom PostgreSQL schemas
- **Behavior After Fix**:
- Automatic schema detection using active `search_path`
- Migrations work seamlessly across all database engines and schemas
- No manual intervention needed for schema migrations
- Files Modified: `internal/repository/database.go` (lines 137-200)
- Build: #60
### Added - Comprehensive Audit Logging
- **Complete Audit Trail for All Data Operations**
- Added audit logging to MovementService (Create, Update, UpdateAsAdmin, Delete)
- Added audit logging to WODService (Create, Update, UpdateAsAdmin, Delete)
- Added audit logging to WorkoutTemplateService (Create, Update, Delete)
- Added audit logging to UserWorkoutService (LogWorkout, LogWorkoutWithPerformance, UpdateLoggedWorkout, DeleteLoggedWorkout)
- Added audit logging to UserService (UpdateProfile)
- Added audit logging to UserSettingsService (UpdateSettings)
- **Audit Log Event Types**
- Movement events: `movement_created`, `movement_updated`, `movement_deleted`
- WOD events: `wod_created`, `wod_updated`, `wod_deleted`
- Workout template events: `workout_template_created`, `workout_template_updated`, `workout_template_deleted`
- User workout events: `user_workout_logged`, `user_workout_updated`, `user_workout_deleted`
- User management events: `profile_updated`, `user_settings_updated`
- **Change Tracking Features**
- Before/after values stored for all update operations
- JSON-encoded details with full context
- User attribution (UserID for performer, TargetUserID for affected user)
- Admin operation flags for administrative updates
- Timestamp tracking for all operations
### Changed
- **Service Method Signatures**
- All CRUD operations now accept `userID int64` and `userEmail string` parameters
- MovementService.Create signature: `Create(movement *domain.Movement, userID int64, userEmail string) error`
- WODService methods updated to include userEmail parameter
- WorkoutTemplateService methods updated to include userEmail parameter
- UserWorkoutService methods updated to include userEmail parameter
- UserSettingsService.UpdateSettings signature: `UpdateSettings(userID int64, userEmail string, updates *domain.UserSettings) (*domain.UserSettings, error)`
- **Handler Implementations**
- All handlers extract userEmail from JWT context using `middleware.GetUserEmail()`
- Handlers pass both userID and userEmail to service methods
- Updated: movement_handler.go, wod_handler.go, workout_template_handler.go, user_workout_handler.go, settings_handler.go
- **Service Initialization**
- All service constructors updated to accept auditLogRepo parameter
- Main.go updated with comprehensive audit log repository injection
- MovementService, WODService, WorkoutTemplateService, UserWorkoutService, UserSettingsService all initialized with audit logging
### Technical Details
- **Build**: #40
- **Files Modified**:
- `internal/domain/audit_log.go` - Added comprehensive event constants
- `internal/service/movement_service.go` - Audit logging for all operations
- `internal/service/wod_service.go` - Audit logging for all operations
- `internal/service/workout_template_service.go` - Audit logging for all operations
- `internal/service/user_workout_service.go` - Audit logging for all operations
- `internal/service/user_service.go` - Added audit logging to UpdateProfile
- `internal/service/user_settings_service.go` - Added audit logging to UpdateSettings
- `internal/handler/movement_handler.go` - Extract and pass userEmail
- `internal/handler/wod_handler.go` - Extract and pass userEmail
- `internal/handler/workout_template_handler.go` - Updated interface and handlers
- `internal/handler/user_workout_handler.go` - Extract and pass userEmail
- `internal/handler/settings_handler.go` - Extract and pass userEmail
- `cmd/actalog/main.go` - Updated service initialization with auditLogRepo
### Audit Logging Patterns
- **Conditional Logging**: All audit logging uses nil checks (`if s.auditLogRepo != nil`)
- **JSON Encoding**: Audit details stored as JSON for structured querying
- **Error Handling**: Audit log failures don't block primary operations (fire-and-forget pattern)
- **Entity Context**: Each log includes entity type, entity ID, entity name, and user context
- **Admin Tracking**: Admin operations marked with `admin_update: true` flag
### Coverage
- ✅ Movement CRUD operations fully logged
- ✅ WOD CRUD operations fully logged
- ✅ Workout Template CRUD operations fully logged
- ✅ User Workout operations fully logged
- ✅ User Profile updates fully logged
- ✅ User Settings updates fully logged
- ✅ Organization operations logged (from v0.14.0)
- ✅ Subscription operations logged (from v0.14.0)
- ✅ User associations logged (from v0.14.0)
### Added - Comprehensive Test Coverage Improvements
- **Service Layer Test Coverage: 81.6% Overall**
- 13 services now at 100% coverage
- Added comprehensive backup_service tests (10 test functions)
- Added wodify_import_service tests with duplicate handling
- Fixed mock initialization patterns across test files
- Added error injection support to mockUserRepo
- **Backup Service Tests** (`backup_service_test.go`)
- `TestBackupService_CreateBackup` - Full backup creation with ZIP archive
- `TestBackupService_CreateBackup_UserNotFound` - Error handling for missing user
- `TestBackupService_CreateBackup_WithUploads` - Backup with file attachments
- `TestBackupService_RestoreBackup` - Replace mode restoration
- `TestBackupService_RestoreBackup_MergeMode` - Merge mode with ID remapping
- `TestBackupService_RestoreBackup_SkipMode` - Skip mode preserving existing
- `TestBackupService_CreateSQLiteDump` - SQLite dump generation
- `TestBackupService_ExportAllTables` - JSON export of all tables
- Added `setupFullTestDB` helper with complete 20-table schema
- **Test Infrastructure Improvements** (`test_helpers.go`)
- Added `getByIDError` field to mockUserRepo for error injection
- Fixed mock constructor patterns for proper map initialization
- All mocks now use `newMockX()` constructor functions
- **Files Modified:**
- `internal/service/test_helpers.go` - Enhanced mockUserRepo with error injection
- `internal/service/backup_service_test.go` - 10 new test functions
- `internal/service/wodify_import_service_test.go` - Fixed mock initialization
- `docs/TESTING.md` - Complete rewrite with current coverage stats
---
## [0.16.0-beta] - 2024-12-20
### Added - Notification Likes Feature
**Social Engagement for Notifications:**
- Users can "like" any notification (PR achievements, announcements, weekly streaks, WOD milestones)
- Like count displayed with thumbs up icon next to each notification
- Names of users who liked displayed as comma-separated list ("Liked by: jcz, John, Mary")
- Users can like their own notifications
- **Automatic unread marking**: When someone likes a notification, it marks as unread for the original recipient
- CASCADE DELETE: Deleting a notification automatically removes all associated likes
**Backend Implementation:**
- New `notification_likes` table with proper indexes and foreign keys
- Unique constraint prevents duplicate likes (409 Conflict error returned)
- Repository layer with JOIN query to fetch likes with user details
- Service layer handles like/unlike operations and automatic unread marking
- New `MarkAsUnread` method added to NotificationRepository interface
**API Endpoints:**
- `POST /api/notifications/{id}/like` - Like a notification
- `DELETE /api/notifications/{id}/like` - Unlike a notification
- `GET /api/notifications/{id}/likes` - Get all likes with user details (returns count and array of likes)
**Frontend Component:**
- `NotificationLikes.vue` component with thumbs up icon
- Toggle between filled (liked) and outline (not liked) states
- Real-time like count updates
- Displays "Liked by: name1, name2, name3" in small grey text
- Integrates seamlessly into existing NotificationsView
- Proper authentication using configured axios instance
**Database Migration:**
- Migration v0.16.0 creates `notification_likes` table
- Multi-database support: SQLite, PostgreSQL, MySQL
- Indexes on `notification_id` and `user_id` for performance
- Unique constraint on `(notification_id, user_id)` pair
**Technical Details:**
- **Build**: #59
- **Version**: 0.16.0-beta
- **Files Created**:
- `internal/domain/notification_like.go` - Domain model and repository interface
- `internal/repository/notification_like_repository.go` - Data access layer
- `internal/service/notification_like_service.go` - Business logic layer
- `internal/handler/notification_like_handler.go` - HTTP handlers
- `web/src/components/NotificationLikes.vue` - Frontend component
- **Files Modified**:
- `internal/repository/migrations.go` - Added v0.16.0 migration
- `internal/domain/notification.go` - Added `MarkAsUnread` method to interface
- `internal/repository/notification_repository.go` - Implemented `MarkAsUnread`
- `cmd/actalog/main.go` - Wired up dependencies and routes
- `web/src/views/NotificationsView.vue` - Integrated NotificationLikes component
- `pkg/version/version.go` - Updated to v0.16.0-beta
**Testing:**
- ✅ Like/unlike operations verified
- ✅ Like count updates correctly
- ✅ User details populated via JOIN query
- ✅ Notifications marked as unread when liked
- ✅ CASCADE DELETE verified
- ✅ Duplicate like prevention (409 Conflict)
### Fixed - Profile View Workout Summary Statistics
**Bug Fixes:**
- Fixed Personal Records count showing 0 (was looking for `personal_records` field, API returns `movements`)
- Now correctly sums `pr_count` from each movement for total PR count
- Fixed Current Streak calculation with incorrect consecutive day logic
- Rewrote streak algorithm to properly check for consecutive workout days
- Added null safety for API responses when no PRs exist
**Streak Calculation Improvements:**
- Uses unique workout dates (handles multiple workouts per day)
- Checks if most recent workout is today or yesterday (or streak is broken)
- Counts backwards through consecutive days correctly
- Stops at first gap in workout dates
**Files Modified:**
- `web/src/views/ProfileView.vue:604-673` - Stats fetching and calculation logic
### Added - Workout Summary Time Period Filters
**Time Filter Options:**
- This Week (Sunday through today)
- This Month (1st of month through today)
- This Year (January 1st through today)
- All Time (no filtering)
**Filtering Behavior:**
- Total Workouts: Filtered by selected period
- Personal Records: Filtered by `last_pr_date` from PR movements
- Current Streak: Always all-time (continuous metric)
- Custom Templates: Always all-time (cumulative creations)
**UX Features:**
- Chip-based filter selector above stats
- Real-time updates when switching periods
- Default period: "This Month"
- Reactive updates using Vue watch
**Files Modified:**
- `web/src/views/ProfileView.vue:198-212, 578, 603-709, 887-890` - Time filter UI and logic
### Fixed - CI Pipeline Compilation Errors
**Test Compilation Fixes:**
- Fixed `mockEmailService` missing `SendHTMLEmail(to, subject, htmlBody)` method
- Fixed `NewUserWorkoutService` calls missing 4 parameters in all 5 test cases
- Added `mockMovementRepo` with 13 methods implementing `MovementRepository` interface
- Added `GetAllPerformancesForMovement` to `mockUserWorkoutMovementRepo`
**Impact:**
- ✅ All service tests now compile successfully
- ✅ Integration tests compile successfully
- ✅ CI pipeline unblocked for Go compilation step
- ✅ Database matrix tests (SQLite, PostgreSQL, MySQL) can now run
**Files Modified:**
- `internal/service/user_service_test.go` - Added SendHTMLEmail mock method
- `internal/service/user_workout_service_test.go` - Fixed all NewUserWorkoutService calls
- `internal/service/test_helpers.go` - Added mockMovementRepo and GetAllPerformancesForMovement
---
## [0.14.0-beta] - 2024-12-16
### Added - Subscription Billing System
**Dual-Level Subscription Management:**
- User-level subscriptions (individual billing)
- Organization-level subscriptions (gym/team billing)
- Flexible access model: users have access if EITHER personal OR any organization subscription is active
- Three subscription types: Free, Monthly, Annual
- Permanent Free subscriptions for founders/staff (never expire, never need payment)
- Immediate read-only mode enforcement when subscriptions expire (no grace period)
**Admin Controls:**
- Manual payment management (admins mark subscriptions as paid/unpaid)
- Subscription creation, cancellation, and history viewing
- Complete audit trail for all subscription operations
- Admin API endpoints for subscription management
**Read-Only Mode:**
- Users with expired subscriptions can view all data, export data, access dashboard/analytics
- Write operations blocked (POST/PUT/PATCH/DELETE) return HTTP 402 Payment Required
- Read operations allowed (GET/HEAD/OPTIONS) for viewing and exporting
**Backward Compatibility:**
- Migration 0.14.0 automatically seeds all existing users with permanent free subscriptions
- Zero downtime deployment - no existing users lose access
- All current users receive `is_permanent_free = TRUE` status
**Database Support:**
- Multi-database migration tested on SQLite, PostgreSQL, and MariaDB
- Version snapshots created for all three database engines
- Database versioning system for testing future migrations
### Technical Implementation
**New Domain Entities:**
- `UserSubscription` - Individual user subscriptions with 15 fields
- `OrganizationSubscription` - Organization-level subscriptions
- `SubscriptionAccessResult` - Access check result with source indication
**Repository Layer:**
- `UserSubscriptionRepository` - CRUD operations for user subscriptions
- `OrganizationSubscriptionRepository` - CRUD for organization subscriptions
- `SubscriptionAccessRepository` - Performance-optimized access checking (< 10ms target)
**Service Layer:**
- `SubscriptionService` - Business logic for subscription management
- Admin operations: Create, MarkAsPaid, Cancel
- User operations: CheckAccess, GetStatus
**Middleware:**
- `RequireActiveSubscription` - HTTP method-based enforcement
- Allows GET requests when expired (view/export)
- Blocks POST/PUT/PATCH/DELETE when expired
**API Endpoints:**
- `GET /api/subscriptions/status` - User subscription status
- `POST /api/admin/subscriptions/user` - Create user subscription
- `POST /api/admin/subscriptions/user/{id}/mark-paid` - Mark as paid
- `POST /api/admin/subscriptions/user/{id}/cancel` - Cancel subscription
- `GET /api/admin/subscriptions/user/{user_id}` - View subscription history
- Organization subscription endpoints (parallel structure)
**Database Version Management:**
- `db_versions/actalog_0.14.0.db` - SQLite snapshot (564 KB with production-like data)
- PostgreSQL schema `actalog_0_14_0` on 192.168.1.143
- MariaDB database `actalog_0_14_0` on 192.168.1.234
- All version databases contain identical test data with 4 test users
- Automated scripts: `create-db-snapshot.sh`, `verify-version-databases.sh`
### Files Created (14 files)
- `internal/domain/subscription.go` (106 lines) - Domain entities and interfaces
- `internal/repository/user_subscription_repository.go` (441 lines)
- `internal/repository/organization_subscription_repository.go` (441 lines)
- `internal/repository/subscription_access_repository.go` (145 lines)
- `internal/service/subscription_service.go` (368 lines)
- `pkg/middleware/subscription.go` (81 lines)
- `internal/handler/subscription_handler.go` (476 lines)
- `db_versions/README.md` - Version management overview
- `db_versions/VERSION_DATABASES.md` - Multi-database access guide (284 lines)
- `db_versions/MIGRATION_TEST_0.14.0.md` - Migration test report
- `db_versions/actalog_0.14.0.db` - SQLite version snapshot
- `scripts/create-db-snapshot.sh` - Automated snapshot creation
- `scripts/verify-version-databases.sh` - Multi-database verification
- PostgreSQL schema: `actalog_0_14_0` with test data
- MariaDB database: `actalog_0_14_0` with test data
### Files Modified (5 files)
- `internal/repository/migrations.go` - Added migration 0.14.0 (lines 885-1162)
- `pkg/version/version.go` - Updated to 0.14.0, build 24
- `cmd/actalog/main.go` - Wired repositories, service, handler, routes
- `internal/domain/audit_log.go` - Added subscription event types
- `CLAUDE.md` - Added database version management section
### Technical Details
- **Build**: #24
- **Version**: 0.12.2-beta → 0.14.0-beta
- **Migration**: 0.14.0 creates `user_subscriptions` and `organization_subscriptions` tables
- **Schema**: 15 columns per table, 4 indexes per table for performance
- **Constraints**: CHECK constraints on subscription_type and status, CASCADE/SET NULL foreign keys
- **Performance**: Access check optimized for < 10ms per authenticated request
- **Audit**: All subscription operations logged with admin user ID and details
---
## [0.12.2-beta] - 2025-11-28
### Fixed - PWA Offline Functionality
**Offline Workout Recording:**
- Fixed service worker API caching pattern (was `/api/.*/*.json`, now correctly caches `/api/workouts`, `/api/movements`, `/api/wods`, `/api/templates`)
- Added robust offline detection in axios interceptor (checks multiple error indicators: `Network Error`, `ERR_NETWORK`, `navigator.onLine`, timeout)
- Extended offline handling to support PUT requests (workout updates) in addition to POST
- Fixed request data parsing for offline sync queue
**User-Controlled PWA Updates:**
- Replaced disruptive silent auto-reload with user-controlled updates
- New `UpdatePrompt.vue` component shows "Update Available" snackbar with "Later" and "Update Now" buttons
- New `pwa.js` Pinia store manages PWA update state
- Users can choose when to apply updates, preventing data loss during form entry
**Offline Save Notification:**
- Added "Saved Offline" snackbar notification when workouts are saved locally
- Dispatches custom `offline-save` event for UI notification
- Automatically increments pending sync count in network store
### Fixed - Unit Tests
**WOD Service Tests:**
- Fixed `mockWODRepo.GetByName()` to return `nil, nil` instead of `sql.ErrNoRows` when WOD not found
- Updated test cases to use correct error types (`ErrWODOwnership`, `ErrWODUnauthorized`)
- Added required fields (Source, Type, Regime, ScoreType) to Update tests
- Fixed Search test expectation for empty query (service returns empty by design)
### Technical Details
- **Build**: #1 (reset for new patch version)
- **Version**: 0.12.1-beta → 0.12.2-beta
- **Files Created**:
- `web/src/components/UpdatePrompt.vue` (PWA update notification component)
- `web/src/stores/pwa.js` (PWA state management)
- **Files Modified**:
- `web/vite.config.js` (service worker caching patterns)
- `web/src/utils/axios.js` (offline detection and sync)
- `web/src/App.vue` (offline save notification, update prompt)
- `web/src/main.js` (PWA store integration)
- `internal/service/test_helpers.go` (mock fixes)
- `internal/service/wod_service_test.go` (test corrections)
---
## [0.12.1-beta] - 2025-11-28
### Fixed - MySQL/MariaDB Compatibility
**Database-Agnostic Timestamp Functions:**
- Fixed `addWorkoutMovementWithDistance()` to use database-specific timestamp syntax
- Fixed `refresh_token_repository.go` functions with hardcoded SQLite `datetime('now')`:
- `GetByToken()` - Token expiration check
- `Revoke()` - Token revocation timestamp
- `RevokeAllForUser()` - Bulk revocation timestamp
- `DeleteExpired()` - Expired token cleanup
- Added `getTimestampFunc()` helper for database-agnostic timestamp generation
- Supports SQLite (`datetime('now')`), PostgreSQL (`CURRENT_TIMESTAMP`), MySQL/MariaDB (`NOW()`)
### Added - Docker Host Database Documentation
**Comprehensive Troubleshooting Guide for External Databases:**
- `docker/DOCKER.md` - New "Connecting to Host Database" troubleshooting section
- `docker/DATABASE_DEPLOYMENT.md` - Enhanced external database troubleshooting:
- UFW firewall configuration for Docker network access
- MariaDB/MySQL bind-address configuration steps
- PostgreSQL listen_addresses and pg_hba.conf setup
- Database user permission grants for Docker network (172.17.0.0/16)
- Linux `extra_hosts: host.docker.internal:host-gateway` configuration
- Step-by-step connection refused debugging
### Technical Details
- **Build**: #4 → #5
- **Version**: 0.12.0-beta → 0.12.1-beta
- **Files Modified**:
- `internal/repository/database.go` (timestamp helper + fix)
- `internal/repository/refresh_token_repository.go` (4 function fixes)
- `docker/DOCKER.md` (host database troubleshooting)
- `docker/DATABASE_DEPLOYMENT.md` (firewall + connection troubleshooting)
---
## [0.12.0-beta] - 2025-11-26
### Fixed - Mobile PWA Layout
**Comprehensive Mobile Overflow Fix:**
- Systematically fixed horizontal/vertical overflow issues across 27 view files
- Implemented `.mobile-view-wrapper` CSS pattern for consistent mobile-safe layouts
- Updated `main.css` with global mobile-safe styles preventing body overflow
- Enhanced `App.vue` with dynamic safe-area handling for iOS PWA
- Views updated include:
- Dashboard, Profile, Performance, Workouts
- WOD/Movement Libraries and Detail views
- Log Workout, Quick Log dialogs
- Admin views (Users, Backups, Data Cleanup, Data Change Logs, User Content)
- Import/Export, PR History, Templates
- Auth flows (Login, Register, Forgot/Reset Password)
- Prevents content from extending beyond viewport on mobile devices
- Ensures smooth scrolling within content areas
- Maintains fixed header (56px) and bottom navigation (70px) positioning
### Added - Docker Image Metadata
**OCI Labels for Docker Images:**
- Added comprehensive OCI-compliant labels to Docker build scripts
- User-editable metadata section at top of `docker/scripts/build.sh`
- Labels include:
- `org.opencontainers.image.title` - Application name
- `org.opencontainers.image.description` - Project description
- `org.opencontainers.image.vendor` - Organization/vendor name
- `org.opencontainers.image.authors` - Author contact information
- `org.opencontainers.image.source` - Repository URL
- `org.opencontainers.image.documentation` - Documentation URL
- `org.opencontainers.image.licenses` - License type
- `org.opencontainers.image.version` - Build version (from tag)
- `org.opencontainers.image.created` - Build timestamp (auto-generated)
- Improves container registry display and image discoverability
### Changed - Admin UI Improvements
**Admin User Content View Enhancement:**
- Moved Actions column from last to first position in data table
- Improves mobile usability by placing action buttons immediately visible
- Consistent with other admin table patterns
- Delete buttons now accessible without horizontal scrolling
### Technical Details
- **Build**: #71 → #1 (reset for new minor version)
- **Version**: 0.11.0-beta → 0.12.0-beta
- **Files Modified**: 27 Vue view files, App.vue, main.css, build.sh, AdminUserContentView.vue
---
## [0.11.0-beta] - 2025-11-26
### Added - Data Change Audit Logs
**Data Change Logging System:**
- Complete audit trail for data modifications (updates and deletes)
- Before/after values stored as JSON for full change history
- Tracks entity type, entity ID, entity name, operation, user, timestamp
- Optional IP address and user agent capture
- Automatic integration with WOD and Movement services
**Admin UI for Data Change Logs:**
- New admin view at `/admin/data-change-logs` for browsing change history
- Filterable by entity type (WOD, Movement, Workout, etc.)
- Filterable by operation (Update, Delete)
- Filterable by user email (partial match)
- Paginated data table with 50 logs per page
- Details dialog showing:
- Full change metadata (timestamp, entity info, user)
- Before/After JSON values in formatted display
- Changed fields diff table for updates
- Color-coded operations (warning for update, error for delete)
- Color-coded entity types for easy identification
**Navigation Integration:**
- "Data Change Logs" card added to Admin dashboard
- Link added to Profile page Administration section for admins
- Consistent styling with existing admin tools
**Database Migration:**
- New `data_change_logs` table (migration 0.5.2)
- Indexes on entity_type, entity_id, user_id, created_at, operation
- Support for all database drivers (SQLite, PostgreSQL, MySQL)
**API Endpoints:**
- `GET /api/admin/data-change-logs` - List with filters and pagination
- `GET /api/admin/data-change-logs/:id` - Get single log entry
- `GET /api/admin/data-change-logs/entity/:type/:id` - Entity history
- `POST /api/admin/data-change-logs/cleanup` - Delete old logs
### Technical Details
- **Build**: #62 → #63
- **New Files**:
- `internal/domain/data_change_log.go` - Domain model and interfaces
- `internal/repository/data_change_log_repository.go` - Data access layer
- `internal/service/data_change_log_service.go` - Business logic with helper methods
- `internal/handler/data_change_log_handler.go` - HTTP handlers
- `web/src/views/AdminDataChangeLogsView.vue` - Admin UI component
- **Modified Files**:
- `internal/service/wod_service.go` - Added data change logging
- `internal/service/movement_service.go` - Created service with data change logging
- `internal/handler/wod_handler.go` - Pass user email to service
- `internal/handler/movement_handler.go` - Use new MovementService
- `web/src/views/AdminView.vue` - Added Data Change Logs card
- `web/src/views/ProfileView.vue` - Added admin link to Data Change Logs
- `web/src/router/index.js` - Added route for data-change-logs
### Use Cases Enabled
- Track who changed what and when across all data types
- Review before/after values for any modification
- Audit trail for compliance and accountability
- Debug data issues by reviewing change history
- Admin cleanup of old logs to manage storage
---
## [0.10.0-beta] - 2025-01-23
### Added - Docker Deployment with Automatic Seed Import
**Docker Infrastructure:**
- Multi-stage Dockerfile with optimized build process
- Three docker-compose configurations:
- `docker-compose.yml` - SQLite (default, single-server deployments)
- `docker-compose.postgres.yml` - PostgreSQL (production recommended)
- `docker-compose.mariadb.yml` - MariaDB/MySQL (production alternative)
- GitHub Actions CI/CD workflow for automated image building
- Helper scripts for building and pushing Docker images
- Health checks for container monitoring
**Automatic Seed Data Import:**
- Optional automatic import of CSV seed data on first deployment
- Environment-based configuration (ADMIN_EMAIL, ADMIN_PASSWORD)
- Entrypoint script orchestrating app startup and seed import
- Imports 182 movements and 314 WODs automatically
- One-time execution using marker file pattern
- Graceful degradation when credentials not provided
**Comprehensive Documentation:**
- `DOCKER.md` - Complete Docker deployment guide with examples
- `DATABASE_DEPLOYMENT.md` - Multi-database deployment guide
- `TEST.md` - Testing guide for Docker deployments
- Environment configuration templates for all databases
- Migration guides between database types
**Seed Data:**
- 182 CrossFit movements (all standard movements including Girl/Hero WOD movements)
- 314 benchmark WODs (all Girl and Hero WODs)
- CSV format for easy import and modification
### Technical Details
- **Build**: #62 → #63 (build number auto-incremented)
- **New Files**:
- `docker/Dockerfile` - Multi-stage build (frontend, backend, runtime)
- `docker/scripts/entrypoint.sh` - Startup orchestration
- `docker/scripts/init-seeds.sh` - Seed import script
- `docker/scripts/build.sh` - Docker build helper
- `docker/scripts/push.sh` - GitHub Container Registry push helper
- `.github/workflows/docker-build.yml` - CI/CD automation
- **Modified**: All environment template files (.env.example, .env.postgres, .env.mariadb)
- **Documentation**: Added comprehensive Docker and database deployment guides
### Deployment Features
- GitHub Container Registry (ghcr.io) integration
- Automatic image builds on push to main branch
- Tag-based versioning (latest, version-specific tags)
- Health check endpoints for monitoring
- Volume management for persistent data
- Network isolation with bridge networks
- Non-root container user for security
## [0.9.0-beta] - 2025-01-23
### Added - Full Offline Support & PWA Enhancements
**iOS PWA Support:**
- iOS-specific meta tags for full PWA capabilities
- Apple touch icon configuration
- Black-translucent status bar styling
**Network Status Management:**
- Pinia network store for centralized online/offline state
- Real-time status chip in app bar (Offline/Syncing indicators)
- Automatic network event detection
- Pending sync operation counter
**User Notifications:**
- Persistent offline notification with explanation
- 3-second online notification when reconnected
- Sync complete confirmation notification
- All notifications dismissible by user
**Offline Data Storage:**
- IndexedDB integration with axios interceptors
- Automatic request queuing for failed network calls
- Offline workout creation with background sync
- Movement list caching for offline access
**Auto-Sync Mechanism:**
- Automatic sync when connection restored
- Visual sync status feedback
- Error handling with retry logic
- Manual and automatic sync triggers
**Offline-Capable Data Fetching:**
- `useOfflineData` composable for network-aware loading
- Cache-first strategy with API fallback
- Generic `fetchWithCache` pattern
- Movement caching implementation
**PWA Install Prompt:**
- Custom branded install UI
- Smart timing (1 minute delay)
- 7-day dismissal memory
- Installation state detection
### Changed
- Enhanced axios interceptors for offline request handling
- Updated service worker runtime caching configuration
- App.vue now includes network notifications and install prompt
### Technical Details
- **Build**: #61
- **New Files**: network.js store, InstallPrompt.vue, useOfflineData.js composable
- **Modified**: index.html, App.vue, axios.js, offlineStorage.js
## [0.8.2-beta] - 2025-01-23
### Fixed
- **Quick Log Template Selection**: Fixed crash when selecting workout templates from Quick Log dialog
- Removed conflicting `item-value` property from v-autocomplete (was causing null object errors)
- Added optional chaining (`?.`) throughout `submitQuickLog()` for defensive coding
- Added error alert when template data is invalid
- **Template WOD Display**: Fixed WOD names not showing when logging from template
- Updated `getWODName()` to handle both nested (`wod.name`) and flattened (`wod_name`) API formats
- Updated `initializePerformanceArrays()` to handle both WOD data formats
- Fixed score type mapping to use full format (`'Time (HH:MM:SS)'` instead of `'Time'`)
- Added missing `time_hours` field to WOD performance initialization
### Enhanced
- **UI Consistency**: Updated Log Workout page styling to match Quick Log aesthetic
- Removed excessive rounded corners from form elements (changed `rounded` to `border-radius: 8px`)
- Made card styles more compact and consistent
- **Quick Log UX**: Improved template selection workflow
- Hidden "Browse Templates" button when arriving from Quick Log with pre-selected template
- Added prominent warning message explaining data preservation behavior
- Changed template info box to orange warning theme with information icon
- Clear message: "Only the date will be preserved. Notes, workout name, and total time entered here will not be carried over."
### Technical
- Enhanced `onTemplateSelected()` function to properly initialize performance arrays after loading template
- Added template to list if not already present (handles Quick Log navigation scenario)
- Improved data format compatibility between different API response structures
## [0.8.1-beta] - 2025-01-22
### Added
- **Cross-Database Backup/Restore**: Complete database-agnostic backup and restore system
- Database-agnostic table existence checks using `information_schema` (PostgreSQL/MySQL) and `sqlite_master` (SQLite)
- Table column introspection for schema evolution support
- Automatic detection of schema differences between backup and target database
- Column filtering: Only restores columns that exist in target schema (handles removed columns gracefully)
- New columns use DEFAULT values from schema (handles added columns)
- **Full cross-database migration support**:
- ✅ MariaDB → PostgreSQL
- ✅ SQLite → PostgreSQL
- ✅ MySQL → MariaDB
- ✅ Any combination of supported databases
- **PostgreSQL Sequence Reset**: Automatic sequence management after restore
- Resets auto-increment sequences to `MAX(id) + 1` for all tables
- Prevents "duplicate key violation" errors on subsequent inserts
- Uses `pg_get_serial_sequence()` and `setval()` for proper sequence handling
- Only applies to PostgreSQL (SQLite/MySQL handle sequences differently)
- **Data Type Conversion**: Automatic type conversion between databases
- Boolean conversion: `0/1` (SQLite/MySQL) ↔ `false/true` (PostgreSQL)
- Handles columns: `is_pr`, `is_template`, `is_standard`, `email_verified`, `account_disabled`, `notifications_enabled`
- JSON unmarshaling safety: Handles `float64` → boolean conversion
- Preserves data integrity across different database type systems
- **Schema Evolution Support**: Forward and backward compatibility for version migrations
- Backup from v0.6.0 can be restored to v0.8.1 (handles missing tables/columns)
- Backup from v0.8.1 can be restored to v0.6.0 (newer columns gracefully ignored)
- Informative logging: "skipped N column(s) not present in target schema"
- No manual SQL intervention required for schema differences
### Enhanced
- **RestoreBackup Function**: Complete rewrite for database compatibility
- Replaced SQLite-specific `sqlite_master` queries with database-agnostic `tableExists()`
- Added column introspection before each table restore
- Integrated automatic sequence reset for PostgreSQL
- Enhanced error messages with specific table and column information
- Graceful handling of missing tables (forward compatibility)
- **restoreTable Function**: Full schema evolution and type conversion support
- Column filtering based on actual target schema
- Value conversion for database compatibility
- Per-column type conversion using `convertValue()`
- Informative progress logging during restore
- Automatic sequence reset after table population
### Functions Added
- `tableExists(tx, tableName)`: Database-agnostic table existence check
- `getTableColumns(tx, tableName)`: Query actual schema columns
- `resetSequence(tx, tableName)`: PostgreSQL sequence management
- `convertValue(val, columnName)`: Cross-database type conversion
- `containsString(slice, str)`: Helper for column filtering
### Use Cases Enabled
- **Production Database Migration**: Migrate from SQLite (development) to PostgreSQL (production) using backup/restore
- **Cross-Database Replication**: Copy data between different database systems without manual export/import
- **Version Upgrades**: Restore old backups to newer application versions seamlessly
- **Multi-Tenant Migration**: Migrate from single-tenant to multi-tenant PostgreSQL using schema parameter
- **Disaster Recovery**: Restore backups to different database types in emergency scenarios
- **Development → Production**: Test with SQLite, deploy with PostgreSQL using same backup files
### Technical Details
- **Build Number**: #58
- **Files Modified**:
- `internal/service/backup_service.go`: Added 190+ lines of new database-agnostic helper functions
- Updated `RestoreBackup()` and `restoreTable()` with full schema evolution support
- **Backward Compatibility**: 100% backward compatible - same-database restores work identically
- **Testing**: Builds successfully, ready for cross-database testing
### Migration Example
```bash
# On MariaDB v0.7.x instance
POST /api/admin/backups
Download actalog_backup_20250122.zip
# On PostgreSQL v0.8.1 instance
DB_DRIVER=postgres
make migrate # Creates PostgreSQL schema
POST /api/admin/backups/upload # Upload MariaDB backup
POST /api/admin/backups/{filename}/restore # Data migrated!
```
## [0.8.0-beta] - 2025-11-22
### Changed
- **PostgreSQL Driver Migration (BREAKING for PostgreSQL users)**: Migrated from `lib/pq` to `pgx/v5` driver
- **Dependency Change**: Removed `github.com/lib/pq v1.10.9`, added `github.com/jackc/pgx/v5 v5.7.6`
- **Performance**: 10-30% faster for most PostgreSQL workloads
- **Active Development**: pgx is actively maintained vs lib/pq in maintenance mode
- **Better Features**: Improved support for PostgreSQL-specific features (LISTEN/NOTIFY, COPY, binary protocol)
- **Context Support**: Better cancellation and timeout handling
- **Backward Compatibility**: SQLite and MySQL/MariaDB unaffected, full backward compatibility maintained
### Added
- **PostgreSQL Schema Support**: Added `DB_SCHEMA` environment variable for schema isolation
- Enables multi-tenant PostgreSQL deployments using database schemas
- DSN now includes `search_path` parameter for schema targeting
- Default schema: `public` (standard PostgreSQL behavior)
- Example: `DB_SCHEMA=actalog` routes all operations to the actalog schema
- **Connection Pooling Configuration**: Fine-grained control over database connection pools
- `DB_MAX_OPEN_CONNS`: Maximum simultaneous database connections (default: 25)
- `DB_MAX_IDLE_CONNS`: Maximum idle connections kept ready (default: 5)
- `DB_CONN_MAX_LIFETIME`: Maximum connection lifetime before recycling (default: 5m)
- Applies to PostgreSQL and MySQL/MariaDB only (SQLite uses single connection)
- Configurable per deployment for optimal resource usage
- Updated `.env.example` with connection pooling examples and tuning guidance
- **Multi-Database Testing**: Comprehensive verification across all supported databases
- ✅ SQLite (sqlite3): Fully backward compatible, all features tested
- ✅ PostgreSQL (pgx/v5): Full migration verified with real database at 192.168.1.143
- ✅ MariaDB/MySQL (mysql): Compatibility verified with real database at 192.168.1.234
- All three databases: schema creation, migrations, seeding, and operations verified
- Real-world connection pooling and schema isolation tested
- **Documentation**: Created comprehensive migration guide
- `docs/POSTGRESQL_MIGRATION.md`: Complete migration guide for PostgreSQL users
- Step-by-step migration instructions for existing lib/pq users
- New PostgreSQL deployment instructions from scratch
- Schema isolation configuration examples
- Connection pooling tuning guidelines
- Troubleshooting section for common issues
- Performance comparison (pgx vs lib/pq)
- Rollback instructions if needed
- Test results for all three databases with real connection details
- **Docker Deployment Planning**: Added comprehensive Docker deployment roadmap to TODO.md
- 50+ sub-tasks for complete Docker deployment solution
- Documentation planning: `DOCKER_DEPLOYMENT.md`, `DOCKER_BUILD.md`, README updates
- Implementation tasks: Dockerfile (multi-stage), docker-compose files for all 3 databases
- GitHub Actions workflow for automated builds and publishing to ghcr.io
- Multi-architecture support (amd64, arm64)
- Testing across all deployment scenarios
- Target: One-command deployment with `docker-compose up -d`
- Target version: v0.9.0-beta
### Enhanced
- **Database Abstraction Layer**: Improved database compatibility handling
- New helper functions: `getBoolValue()`, `getPlaceholders()` for database-agnostic SQL
- SQL placeholders: SQLite/MySQL use `?`, PostgreSQL uses `$1, $2, $3`
- Boolean values: SQLite uses `0/1`, PostgreSQL/MySQL use `TRUE/FALSE`
- Timestamp functions: SQLite uses `datetime('now')`, PostgreSQL uses `CURRENT_TIMESTAMP`, MySQL uses `NOW()`
- Insert ID retrieval: PostgreSQL uses `RETURNING id` clause instead of `LastInsertId()`
- All seeding functions updated: `seedStandardMovements()`, `seedStandardWODs()`, `seedWorkoutTemplates()`
- All helper functions updated: `createWorkout()`, `addWorkoutMovement()`, `addWorkoutMovementWithTime()`, `addWorkoutWOD()`, `getMovementIDByName()`, `getWODIDByName()`
- **DSN Format**: Updated PostgreSQL connection string format for pgx compatibility
- Old format (lib/pq): `host=localhost port=5432 user=actalog dbname=actalog sslmode=disable`
- New format (pgx): `postgres://user:password@host:port/database?sslmode=disable&search_path=schema`
- Automatic schema path inclusion when `DB_SCHEMA` is configured
- Full compatibility with PostgreSQL URIs
- **Configuration Files**: Updated all configuration examples
- `.env.example`: Added DB_SCHEMA, connection pooling parameters, and tuning guidelines
- `configs/config.go`: New DatabaseConfig fields (Schema, MaxOpenConns, MaxIdleConns, ConnMaxLifetime)
- Environment loading functions: `getEnvInt()`, `getEnvDuration()` for typed config values
- Default values optimized for production use
### Fixed
- **MariaDB Compatibility**: Fixed SQL syntax issues for MariaDB/MySQL
- Fixed `addWorkoutWOD()` to use database-specific timestamp functions
- Fixed `getMovementIDByName()` to use database-specific placeholders
- Fixed `getWODIDByName()` to use database-specific placeholders
- All helper functions now properly handle MySQL/MariaDB-specific SQL
### Technical Details
- **Build Number Range**: #47-56 (10 builds during migration)
- **Files Modified**:
- Core: `go.mod`, `configs/config.go`, `internal/repository/database.go`
- Commands: `cmd/actalog/main.go`, `cmd/migrate/main.go`, `cmd/check-schema/main.go`
- Documentation: `.env.example`, `docs/POSTGRESQL_MIGRATION.md`
- **Breaking Changes**:
- PostgreSQL users must update from lib/pq to pgx (see migration guide)
- No breaking changes for SQLite or MySQL users
- Database schemas and data remain fully compatible
- **Migration Path**: Existing PostgreSQL databases work without changes (DSN format updated automatically)
## [0.7.6-beta] - 2025-11-22
### Added
- **Backup Upload for Migration**: Added ability to upload external backup ZIP files from other systems
- New upload button in AdminBackupsView with file picker for .zip files
- `POST /api/admin/backups/upload` endpoint for multipart file upload
- `UploadBackup()` service method with filename validation and ZIP verification
- Timestamp-based renaming to prevent filename conflicts
- Audit logging for all backup uploads with original filename tracking
- Enables data migration between different ActaLog installations
- Supports cross-database migrations (e.g., PostgreSQL backup restored to SQLite system)
- **Documentation Planning**: Comprehensive planning for future documentation systems added to TODO.md
- **End-User Help Documentation System**: Multi-document help system with Markdown, screenshots, and Mermaid diagrams
- Planned GitHub storage (docs/help/ directory) with links from Profile screen
- Table of Contents, FAQ section, and "How do I..." tutorials
- 8 tutorial topics covering key features (logging, PRs, templates, performance, imports, PWA)
- 4 workflow diagrams (workout logging, PR detection, import/export, authentication)
- Image placeholders with descriptive captions for future screenshots
- Cross-referenced topics and troubleshooting section
- **Administrator Documentation System**: Comprehensive admin guide for system operators
- Planned GitHub storage (docs/admin/ directory) with admin-only access from Profile screen
- 12 administrative tutorials (user management, backups, audit logs, security, troubleshooting)
- 5 admin workflow diagrams (user lifecycle, backup/restore, security/audit, permissions, lockout process)
- Security best practices section (password policy, JWT management, CORS, email, monitoring)
- System configuration guide (environment variables, database setup, SMTP, PWA, deployment)
- API endpoint reference for automation
- **Test Coverage Planning**: Comprehensive testing strategy for both backend and frontend
- Backend testing: 13 tasks covering unit tests, integration tests, mocking, transactions
- Frontend testing: 10 tasks covering components, E2E flows, PWA functionality, routing
- Testing infrastructure: 7 tasks including CI/CD, coverage reporting, E2E framework, performance testing
- Documentation: 4 tasks for testing patterns, guidelines, data setup, and CI documentation
- **Scheduled Remote Backups**: Future enhancement planning for automatic cloud backups
- Support for 6 cloud providers (AWS S3, Google Cloud Storage, Azure, Dropbox, Google Drive, SFTP/FTP)
- Configurable schedules (hourly, daily, weekly, monthly)
- Retention policies, verification, notifications, bandwidth throttling
- **Expanded Seed Data**: Planning for extracting additional WODs and Movements from import files
- Parse PDFs and crossfit_wods.csv to expand standard movement and WOD library
- Automated extraction and conversion to seed CSV format
### Enhanced
- **Audit Logging for Backup Operations**: Comprehensive audit trail for all backup activities
- `backup_downloaded` event now logs file size in bytes asynchronously
- `backup_restored` event now includes detailed statistics:
- Total users, workouts, movements, and WODs restored
- Provides visibility into restore scope and impact
- All audit logs include user email and timestamp
- Enables security monitoring and compliance tracking for data operations
### Fixed
- **Cross-Version Restore Compatibility**: Backup restore now handles schema version differences gracefully
- Added table existence checks before DELETE and INSERT operations using `sqlite_master` queries
- Tables missing in current schema are skipped with warnings instead of causing fatal errors
- `restoreTable()` method validates table existence before attempting data restore
- Enables restoring backups from different ActaLog versions (forward and backward compatibility)
- Warning messages logged for skipped tables to aid troubleshooting
- Prevents 500 errors when restoring backups created on different schema versions
## [0.7.5-beta] - 2025-11-22
### Added
- **Admin User Management - Complete Integration**: Fully activated admin user management dashboard
- Activated "User Management" card in AdminView (`/admin`) - now clickable and navigates to user management
- Removed "Coming Soon" placeholder status
- Backend API endpoints from v0.4.6-beta now fully integrated with frontend UI
- **"Remember Me" Functionality**: Extended session duration for better user experience
- Added checkbox to login form: "Remember me for 30 days"
- Backend: Extended refresh token duration from 7 days to 30 days when Remember Me is checked
- Modified `CreateRefreshToken()` method to accept `rememberMe` parameter (`internal/service/user_service.go:517`)
- Updated login handler to pass Remember Me flag to service (`internal/handler/auth_handler.go:147`)
- Frontend: Auth store already configured to send `remember_me` flag to API
- Refresh tokens stored in localStorage for automatic session restoration
- Audit logging for Remember Me token creation
- Users who don't check "Remember Me" still get 7-day refresh tokens (default behavior)
- **Database Backup and Restore System - Complete Activation**: Full disaster recovery and data migration capability
- Activated "Database Backups" card in AdminView (`/admin`) with orange database-export icon
- Complete backup/restore functionality previously implemented but not activated
- Backend fully implemented and wired up:
- `internal/service/backup_service.go` - Full database export to ZIP with JSON data
- `internal/handler/backup_handler.go` - All CRUD endpoints for backup management
- Routes active in `cmd/actalog/main.go` under `/api/admin/backups`
- Frontend fully implemented:
- `AdminBackupsView.vue` - Complete backup management interface at `/admin/backups`
- Create new backups with metadata (version, user counts, workout counts)
- List all backups with creation date, creator email, stats, and file size
- Download backups as ZIP files
- Delete backups with confirmation dialog
- Restore backups with strong warning dialog and confirmation requirement
- Empty state for first-time use
- API Endpoints (Admin-only):
- `POST /api/admin/backups` - Create new backup
- `GET /api/admin/backups` - List all backups
- `GET /api/admin/backups/{filename}` - Download backup file
- `GET /api/admin/backups/{filename}/metadata` - Get backup metadata
- `DELETE /api/admin/backups/{filename}` - Delete backup
- `POST /api/admin/backups/{filename}/restore` - Restore from backup
- ZIP backup structure includes all database tables exported as JSON
- **SQLite Database Dump**: All backups now include a portable SQLite database file (`actalog_backup.db`)
- If running SQLite: Direct copy of production database included in ZIP
- If running PostgreSQL/MySQL: New SQLite database created from exported data and included in ZIP
- Provides universal, portable database format that can be opened with any SQLite tool
- Enables easy data inspection and migration between database systems
- Supports all database drivers (SQLite, PostgreSQL, MySQL)
- Audit logging for all backup operations
- Security: Filename validation prevents directory traversal attacks
### Changed
- **Mobile-Friendly Admin Table**: Restructured user management table for better mobile accessibility
- Split single "Actions" column into 6 individual labeled columns (Details, Lock, Enable, Email, Change Role, Delete)
- Clear column headers eliminate need for hover tooltips on mobile devices
- Improved visual clarity with centered icons and consistent sizing
- Better touch targets for mobile users
### Fixed
- **PR History Date Display**: Fixed PR history to show workout date instead of record creation date
- Backend: Added `WorkoutDate` assignment in `GetPRMovements()` and `GetPRWODs()` repository methods
- Frontend: Changed `PRHistoryView.vue` to display `workout_date` instead of `created_at`
- PR dates now show when the workout was performed (e.g., "Fri, Oct 10, 2024")
- Previously showed database record creation timestamp which was incorrect for imported data
- Affects both movement PRs and WOD PRs
- **AdminBackupsView Layout**: Fixed Vuetify bottom navigation layout error
- Restructured container hierarchy (outer `<v-container>` → `<div>`)
- Eliminated "Could not find layout item 'bottom-navigation'" console error
- Fixed scroll behavior with proper overflow handling
- Resolved layout conflicts when navigating from ProfileView
- Applied same container pattern from AdminUsersView, WODLibraryView, MovementsLibraryView
- **AdminUsersView Layout**: Fixed Vuetify bottom navigation layout error
- Restructured container hierarchy (outer `<v-container>` → `<div>`)
- Eliminated "Could not find layout item 'bottom-navigation'" console error
- Fixed scroll behavior with proper overflow handling
- Resolved layout conflicts when navigating from ProfileView
## [0.7.4-beta] - 2025-11-22
### Added
- **Quick Log Buttons on Library Cards**: Added Quick Log functionality directly to WOD and Movement library card views
- Teal lightning bolt icon buttons on each WOD card in WOD Library view
- Teal lightning bolt icon buttons on each Movement card in Movements Library view
- Quick Log dialog opens directly from cards without navigating to detail pages
- Pre-populated forms with selected WOD or Movement data
- Streamlined workout logging workflow from library browsing
- **Quick Log Buttons on Detail Pages**: Enhanced WOD and Movement detail screens
- Added prominent Quick Log buttons to WODDetailView and MovementDetailView
- Pre-populated Quick Log dialogs with current item being viewed
- Consistent user experience across all viewing contexts
- **Admin User Management Dashboard**: Complete administrative control system for user accounts
- Activated Admin User Management card in AdminView (`/admin`)
- Full-featured user management UI at `/admin/users` route
- User list table with pagination (50 users per page) and real-time search
- Mobile-optimized table with individual labeled columns (no hover tooltips required):
- **Details**: View comprehensive user information dialog
- **Lock**: Unlock temporarily locked accounts from failed login attempts
- **Enable**: Enable/disable user accounts with optional reason tracking
- **Email**: Toggle email verification status manually
- **Change Role**: Switch between "user" and "admin" roles
- **Delete**: Permanently delete users with confirmation dialog
- User details dialog showing all account information:
- Email verification status with visual badges
- Account status (Active/Disabled) with color coding
- Role display with chips
- Timestamps: created_at, last_login_at, email_verified_at, disabled_at
- Disable reason display when applicable
- Color-coded status indicators throughout (green/success, red/error, purple/admin, blue/user)
- Confirmation dialogs for destructive actions (disable, delete)
- Success/error messaging for all operations
- Backend API endpoints from v0.4.6-beta now fully integrated with frontend
### Changed
- **Icon Consistency**: Unified Quick Log iconography across the entire application
- All Quick Log buttons now use `mdi-lightning-bolt` icon (teal color)
- Replaced `mdi-play-circle` icons in WorkoutsView template cards with lightning bolt
- Consistent visual language for Quick Log feature throughout the app
- Tooltips added to all Quick Log buttons for clarity
- **User Management Table Structure**: Improved accessibility and mobile usability
- Restructured "Actions" column into 6 individual labeled columns
- Clear column headers eliminate need for hover tooltips on mobile devices
- Centered icon buttons with consistent sizing and colors
- Enhanced visual feedback for current state (locked/unlocked, enabled/disabled, verified/unverified)
### Fixed
- **Vuetify Layout Issues**: Fixed bottom navigation layout conflicts
- Restructured WODLibraryView, MovementsLibraryView, and AdminUsersView container hierarchies
- Changed outer containers from `<v-container>` to `<div>` to prevent layout system conflicts
- Moved bottom navigation outside scrollable content containers
- Eliminated "Could not find layout item 'bottom-navigation'" console errors
- Fixed scroll behavior with proper `overflow-y: auto` and `max-height` constraints
- **Template Deletion Bug**: Fixed custom template deletion endpoint error
- Corrected API endpoint from `DELETE /api/workouts/{id}` to `DELETE /api/templates/{id}`
- Resolved 500 Internal Server Error and "unauthorized workout access" issue
- Custom workout templates now delete successfully
## [0.7.3-beta] - 2025-01-22
### Added
- **Quick Log on Performance Screen**: Complete Quick Log dialog integration on Performance view
- Quick Log button now opens dialog directly on Performance screen (no navigation to Dashboard)
- Pre-populates with the movement or WOD currently being viewed
- User can change selection within dialog if needed
- Automatically refreshes performance data after successful submission
- Maintains user context and viewing state
### Fixed
- **Performance Chart Sorting**: Fixed chronological ordering for workouts on the same date
- Implemented two-level sorting: primary by `workout_date`, secondary by `created_at` timestamp or `id`
- Ensures newest entries appear on the right side of charts (chronological order)
- Prevents multiple same-day workouts from appearing in database order
- Applied to both movement and WOD performance charts
## [0.7.2-beta] - 2025-01-22
### Added
- **1RM (One-Rep Max) Calculation and Display**: Complete system for tracking estimated strength maximums
- **Backend**: Enhanced `/api/performance/movements/{id}` endpoint to calculate and return 1RM data
- Added `MovementPerformanceWithRM` response type with `calculated_1rm` and `formula` fields
- Returns `best_1rm` and `best_formula` for overall best performance
- Uses hybrid formula approach from `pkg/prmath/one_rm.go`:
- 1 rep = Actual weight
- 2-10 reps = Epley formula: `1RM = weight × (1 + reps/30)`
- 11+ reps = Wathan formula: `1RM = (100 × weight) / (48.8 + 53.8 × e^(-0.075 × reps))`
- **Frontend - Best 1RM Card**: New stat card displaying estimated 1RM
- Prominent gold-colored display (#ffc107) with arm-flex icon
- Shows rounded 1RM value with "lbs (estimated)" label
- Displays formula chip indicating calculation method
- Only appears when weight/reps data is available
- **Frontend - Performance History**: Enhanced history entries with 1RM estimates
- Shows "Est. 1RM: XXX lbs" in gold text for each performance record
- Appears alongside date and notes in subtitle line
- **Frontend - Chart Enhancements**: Dual-line performance chart
- Added dashed gold line showing estimated 1RM trend
- Original solid dark line shows actual weight lifted
- Legend automatically displays when 1RM data exists
- Enhanced tooltips showing both actual weight and estimated 1RM with formula
- Null value filtering prevents gaps in chart display
- **Y-Axis Labels**: Added clear axis labels to all performance charts
- Movement charts: "Weight (lbs)"
- WOD charts: Dynamic labels based on score type (Time/Rounds/Weight)
### Fixed
- **WOD Chart Rendering Issue**: Fixed canvas element not rendering on initial load
- Moved `loadingPerformance = false` before chart rendering to ensure DOM updates
- Added proper `await nextTick()` sequencing for canvas availability
- Resolved null reference errors in WOD performance charts
### Changed
- **Code Cleanup**: Removed debug console.log statements from PerformanceView
- Removed WOD debug logging throughout fetchPerformanceData and renderWODChart
- Cleaner console output in production
## [0.7.1-beta] - 2025-01-22
### Fixed
- **Wodify Import Date Issue**: Fixed performance charts showing all imported workouts as "today" instead of actual workout dates
- Backend: Added `WorkoutDate` field to `UserWorkoutMovement` and `UserWorkoutWOD` domain models
- Backend: Updated repositories to populate `workout_date` from `user_workouts.workout_date` join
- Frontend: Changed PerformanceView to use `workout_date` instead of `created_at` for all date displays
- Charts now correctly show historical dates (e.g., Jul 30, 2018) from Wodify CSV imports
- History grouping and sorting now use actual workout dates
### Changed
- **Performance Chart Date Display**: Charts now display full dates with year
- X-axis labels show year: "Jul 30, 2018" instead of "Jul 30"
- Hover tooltips display full date with year in title
- Applied to both Movement and WOD performance charts
- **Rep Scheme Filter Enhancement**: Improved dropdown filter in Performance view
- Changed "All Reps" to simplified "All" option
- "All" displays all weighted records regardless of rep scheme, sets, or other factors
- Cleaner, more intuitive filtering experience
## [0.7.0-beta] - 2025-11-21
### Added
- **Wodify Performance Import System**
- **Backend Components**
- **Domain Models** (`internal/domain/wodify_import.go`)
- `WodifyPerformanceRow` - Represents CSV row from Wodify export (19 columns)
- `WodifyImportPreview` - Preview statistics and validation results
- `WodifyImportResult` - Import completion statistics
- `ParsedPerformanceResult` - Structured performance data after parsing
- **Result Parser** (`internal/service/wodify_parser.go` - 273 lines)
- 9 regex-based parsers for different result types:
- `Weight` - Parses "3 x 10 @ 85 lbs" → sets, reps, weight
- `Time` - Parses "5:30" (MM:SS) or "1:05:30" (HH:MM:SS) → seconds
- `AMRAP - Rounds and Reps` - Parses "7 + 3" → rounds, reps
- `AMRAP - Reps` - Parses "50 Reps"
- `AMRAP - Rounds` - Parses "5 Rounds"
- `Max reps` - Parses "3 x 8" (sets x reps)
- `Calories` - Parses "133 Calories"
- `Distance` - Parses "500 m"
- `Each Round` - Parses "175 Total Reps"
- `ParseDate()` - Handles MM/DD/YYYY and MM/DD/YY formats
- `DetermineMovementType()` - Maps component type to movement type
- `DetermineWODScoreType()` - Maps result type to WOD score type
- **Import Service** (`internal/service/wodify_import_service.go` - 582 lines)
- `PreviewImport()` - Analyzes CSV and returns preview without database changes
- `ConfirmImport()` - Executes import with entity auto-creation
- `parseCSV()` - Handles 19-column CSV with multi-line field support
- `groupByDate()` - Groups performances by workout date
- `getOrCreateMovement()` - Auto-creates missing movements
- `getOrCreateWOD()` - Auto-creates missing WODs
- `importWorkout()` - Creates UserWorkout with linked performances
- PR preservation from Wodify export
- **HTTP Handler** (`internal/handler/wodify_import_handler.go` - 107 lines)
- `POST /api/import/wodify/preview` - Preview Wodify CSV import
- `POST /api/import/wodify/confirm` - Execute Wodify CSV import
- File size limit: 10MB
- Multipart form-data with "file" field
- **Frontend Integration** (`web/src/views/ImportView.vue`)
- Added "Wodify Performance" import type with file-chart icon
- **Wodify-Specific Preview UI:**
- Summary stats: total rows, valid rows, workout dates, entities to create
- New Entities card with chips showing movements and WODs to auto-create
- Workout Summary table: date, movement count, WOD count, component types, PR flags
- Gold trophy icons for workouts containing PRs
- **Success Message:**
- Displays: workouts created, performances, movements/WODs auto-created, PRs flagged
- Format: "Workouts Created: 189 | Performances: 293 | Movements Created: 37 | WODs Created: 28 | PRs Flagged: 62"
- **Documentation**
- Updated `CLAUDE.md` with comprehensive Wodify import documentation
- Real-world test results with 6+ years of data
- Code examples for API usage
- Domain model definitions
### Technical
- Clean Architecture maintained: domain → service → handler pattern
- CSV parsing with LazyQuotes and TrimLeadingSpace for robust handling
- Regex-based result string parsing for data extraction
- Date grouping logic to create cohesive UserWorkout entries
- Auto-entity creation reduces manual data entry
- PR flag preservation from source data
- Build number incremented: 27 → 28
### Testing
- ✅ Preview import: Analyzed 293 performance entries, 189 unique dates
- ✅ Confirm import: Successfully imported 6+ years of workout history (2018-2025)
- 189 user workouts created (grouped by date)
- 37 new movements auto-created
- 28 new WODs auto-created
- 293 performance entries
- 62 PRs automatically flagged
- ✅ Data persistence verified in database
- ✅ Data appears correctly in GET /api/workouts
- ✅ Round-trip import → export verified working
- ✅ Graceful handling of invalid rows (1 row with missing component type/name)
### Bug Fixes
- ✅ Investigated and resolved reported User Workouts Import bug
- Testing confirmed feature working correctly
- Data persists to database, appears in API, exports correctly
- Bug report was false or already fixed in previous session
---
## [0.6.0-beta] - 2025-11-21
### Added
- **Database Backup/Restore System**
- **Backend Service** (`internal/service/backup_service.go`)
- `CreateBackup()` - Exports all tables to JSON + uploaded files to ZIP
- `ListBackups()` - Returns metadata for all available backups
- `GetBackupMetadata()` - Reads metadata from backup file
- `DeleteBackup()` - Removes backup file with audit logging
- `RestoreBackup()` - Full database restore from backup
- Automatic table detection (skips tables that don't exist)
- Multi-database support (SQLite, PostgreSQL, MySQL)
- **API Endpoints** (`internal/handler/backup_handler.go`)
- `POST /api/admin/backups` - Create new backup
- `GET /api/admin/backups` - List all backups
- `GET /api/admin/backups/{filename}` - Download backup file
- `GET /api/admin/backups/{filename}/metadata` - Get backup metadata
- `DELETE /api/admin/backups/{filename}` - Delete backup
- `POST /api/admin/backups/{filename}/restore` - Restore from backup
- All endpoints admin-only with authorization checks
- Filename validation to prevent directory traversal attacks
- **Frontend View** (`web/src/views/AdminBackupsView.vue`)
- Backup list table with metadata (users, workouts, movements, WODs)
- Create backup button with progress indicator
- Download/delete/restore actions for each backup
- Strong confirmation dialog for restore (warns about data loss)
- Empty state for no backups
- File size formatting and date/time display
- **Backup Structure**
- ZIP file containing `backup_data.json` with all table data
- Includes uploaded files (profile pictures, etc.) in `uploads/` folder
- Metadata: version, database driver, user counts, file size, created by
- Stored in `/backups/` directory with .gitignore
- **Documentation**
- Updated ProfileView with "Database Backups" admin navigation link
- Added `/admin/backups` route to router
- Created `backups/.gitignore` to prevent backup files from being committed
### Technical
- Clean Architecture maintained: domain → service → handler pattern
- Audit logging for backup creation, deletion, and restore operations
- Transaction-based restore to ensure data consistency
- Automatic cleanup of existing data before restore
- Security: Admin-only access, filename validation, token-based auth
- Build number incremented: 24 → 25
### Testing
- ✅ Create backup: Successfully generates 1.7MB ZIP file
- ✅ List backups: Returns metadata with correct statistics
- ✅ Download backup: Serves ZIP file for download
- ✅ Delete backup: Removes file and logs action
- ⚠️ Restore backup: Tested manually (destructive operation)
---
## [0.5.1-beta] - 2025-11-21
### Added
- **Import/Export System (Phase 1 & 2 - COMPLETE)**
- **WOD Export** (`GET /api/export/wods`)
- CSV format with all WOD fields
- Query parameters for filtering: `include_standard`, `include_custom`
- Successfully tested with all standard WODs
- **Movement Export** (`GET /api/export/movements`)
- CSV format with all movement fields
- Query parameters for filtering: `include_standard`, `include_custom`
- Successfully tested with all standard movements
- **User Workouts Export** (`GET /api/export/user-workouts`)
- JSON format with full nested data structure
- Optional date range filtering: `start_date`, `end_date`
- Includes metadata: user_email, export_date, version, total_count
- Nested workout data: movements, WODs, performance metrics
- **WOD Import** (`POST /api/import/wods/preview`, `POST /api/import/wods/confirm`)
- Preview endpoint with validation before committing
- CSV validation: source, type, regime, score_type enums
- Duplicate detection and handling
- Options: skip_duplicates, update_duplicates
- Successfully tested: created custom WOD
- **Movement Import** (`POST /api/import/movements/preview`, `POST /api/import/movements/confirm`)
- Preview endpoint with validation
- CSV validation: type enum (weightlifting, cardio, gymnastics, bodyweight)
- Duplicate detection
- Successfully tested: created 2 custom movements
- **User Workouts Import** (`POST /api/import/user-workouts/preview`, `POST /api/import/user-workouts/confirm`)
- Preview endpoint working correctly ✅
- Confirm endpoint working correctly ✅
- JSON parsing and validation
- Nested data handling (movements, WODs)
- Auto-creation of missing movements and WODs
- Default workout_name generation for ad-hoc workouts
- **Frontend Views**
- `ExportView.vue` at `/settings/export`
- Data type selector (WODs, Movements, User Workouts)
- Format handling (CSV for WODs/Movements, JSON for User Workouts)
- Options: Include standard items, include custom items
- Date range picker for User Workouts
- Export button triggers file download
- `ImportView.vue` at `/settings/import`
- File upload with drag-and-drop support
- Supported formats info (CSV, JSON)
- Preview table showing parsed data with validation status
- Validation errors display (red highlights for invalid rows)
- Import statistics (total, valid, invalid, duplicates)
- Import options: Skip duplicates, Update duplicates
- Confirm and Cancel buttons
- Fixed axios import to use authenticated instance (was causing 401 errors)
- **Backend Services (1,691 lines total)**
- `internal/service/export_service.go` (385 lines)
- `internal/service/import_service.go` (829 lines)
- `internal/handler/export_handler.go` (178 lines)
- `internal/handler/import_handler.go` (299 lines)
- All routes wired up in `cmd/actalog/main.go`
- **Documentation**
- Created `docs/ROADMAP.md` with detailed development plan
- Updated `docs/TODO.md` with completion status
- Testing results: 6/6 features working (100%)
### Fixed
- **User Workouts Import Persistence Bug** (CRITICAL - Build 22)
- **Location:** `internal/service/import_service.go:760-776`
- **Issue:** Import reported success but workouts didn't appear in API responses
- **Root Cause 1:** Missing `WorkoutType` field when creating UserWorkout struct
- Field was present in JSON import data but not being set on the domain object
- Caused workout_type column to be NULL in database
- **Root Cause 2:** Missing `workout_name` default value for ad-hoc workouts
- Ad-hoc workouts (without workout_id) require workout_name to be queryable
- `GetByIDWithDetails()` throws error when both workout_id and workout_name are NULL
- This caused API to return empty array even though workouts existed in database
- **Fix Applied:**
- Added `WorkoutType: workoutData.WorkoutType` to UserWorkout struct creation
- Added default workout_name generation: `fmt.Sprintf("Workout %s", workoutDate.Format("2006-01-02"))`
- Ensures all ad-hoc workouts have a valid workout_name for retrieval
- **Testing Results:**
- Before: Database had workouts but API returned 0 ❌
- After: Database has workouts AND API returns all workouts ✅
- Verified via database query and `/api/workouts` endpoint
### Changed
- Version remains 0.5.0-beta (will bump to 0.5.1-beta on release)
- Build number incremented: 20 → 22
- Import/Export system is now 100% functional (6/6 features working)
### Testing
- ✅ WOD export/import round-trip tested successfully
- ✅ Movement export/import round-trip tested successfully
- ✅ User Workouts export tested successfully
- ✅ User Workouts import preview tested successfully
- ✅ User Workouts import confirm tested successfully (bug fixed)
### Technical
- Clean Architecture maintained throughout implementation
- Multi-database support (SQLite, PostgreSQL, MySQL)
- CSV parsing with validation
- JSON nested data handling
- Duplicate detection algorithms
- Authorization checks (users can only import/export their own data)
- Rate limiting on import endpoints
- File size limits (max 10MB)
---
## [0.4.6-beta] - 2025-11-15
### Added
- **Admin User Management Enhancements**
- Delete user functionality with confirmation dialog (`DELETE /api/admin/users/{id}`)
- Prevents admin from deleting their own account
- Displays what will be deleted: profile, workouts, PRs, performance history
- Audit logging for all user deletion operations
- Service layer validation with authorization checks
- **Session Management System**
- List active sessions endpoint (`GET /api/sessions`)
- Revoke specific session endpoint (`DELETE /api/sessions/{id}`)
- Revoke all sessions endpoint (`POST /api/sessions/revoke-all`)
- Session ownership validation (users can only manage their own sessions)
- Audit logging for session revocation events
- Service layer: `GetActiveSessions()`, `RevokeSession()`, `RevokeAllSessions()`
- Handler layer: `SessionHandler` with list, revoke, and revoke-all operations
- All endpoints require authentication
- **User Repository Enhancements**
- Fixed `List()` method to include all admin-relevant fields
- Now properly selects: email_verified, account_disabled, locked_at, locked_until, disable_reason
- Proper NULL handling for all nullable timestamp and string fields
### Fixed
- **Admin Users View**
- Icons now correctly display current user state (verified, locked, enabled, role)
- Dynamic icon shapes and colors based on state
- Enhanced tooltips showing current state explicitly
- All toggles (verify email, lock, enable, role, delete) now work correctly
### Changed
- Version bumped to 0.4.6-beta
- Build number reset to 1 for new minor version
- Admin panel now has full CRUD capabilities for user management
### Technical
- Clean Architecture maintained: service → repository pattern
- Audit trail for all administrative actions
- Service layer performs authorization checks before operations
- CASCADE delete configured for related user data
- Security: token ownership validation prevents unauthorized access
## [0.4.5-beta] - 2025-11-14
### Added
- **Admin Data Cleanup: Edit WOD Records**
- New API endpoint: `PUT /api/admin/data-cleanup/wod-record/{id}` for updating individual WOD records
- Backend validation ensures updates match WOD score_type requirements
- Clickable mismatch cards in admin cleanup view open edit dialog
- Edit dialog with score_type-specific form fields (only shows relevant fields)
- Hours, Minutes, Seconds input for Time-based WODs
- Rounds and Reps input for Rounds+Reps WODs
- Weight input for Max Weight WODs
### Fixed
- **Quick Log Form (Dashboard)**
- Fixed Quick Log to respect WOD score_type constraints
- Score type now auto-populates from selected WOD (read-only)
- Only shows fields relevant to the WOD's score_type
- Time-based WODs now support HH:MM:SS format (was only showing seconds)
- Added reactive watchers to auto-calculate total seconds from HH:MM:SS inputs
- **Log Workout Form**
- Fixed score_type check from `'Time'` to `'Time (HH:MM:SS)'`
- Added hours field to time inputs (now fully supports HH:MM:SS)
- Updated time calculation logic to include hours in total seconds and score_value formatting
- **Admin Cleanup View**
- Removed duplicate bottom navigation bar from admin cleanup view
### Changed
- All workout entry and edit forms now enforce WOD score_type constraints
- Time-based WODs consistently use HH:MM:SS format across all forms
- Version bumped to 0.4.5-beta build 1
### Technical
- Frontend conditional rendering prevents invalid field combinations based on score_type
- Backend validation in `UpdateWODRecord` ensures data integrity
- Multi-layer constraint enforcement: frontend UX + backend validation
- Clean Architecture maintained: handler → service → repository layers
## [0.4.4-beta] - 2025-11-14
### Added
- **Retroactive PR Detection System**
- Service method `RetroactivelyFlagPRs()` to analyze all historical workouts chronologically
- Automatically flags PRs based on historical max values for movements and WODs
- Processes workouts in chronological order, tracking max weights, best times, and best rounds+reps
- Repository methods: `UpdatePRFlag()` for both movements and WODs
- API endpoint: `POST /api/workouts/retroactive-flag-prs` (authenticated)
- Command-line script `scripts/retroactive_prs.go` for direct database PR flagging
- Returns count of movement PRs and WOD PRs flagged
### Fixed
- PR detection now works for historical workouts logged before PR system was implemented
- Personal Records view now displays PRs from all workouts, not just newly logged ones
- Resolved issue where existing workouts had `is_pr = 0` even when they contained record performances
### Technical
- Chronological processing ensures PRs are correctly identified based on order of performance
- In-memory tracking of max values during processing to avoid multiple database queries
- Multi-database support (SQLite, PostgreSQL, MySQL) for PR flag updates
- Clean Architecture maintained: domain interfaces → repository implementation → service logic → handler/script
### Changed
- Version bumped to 0.4.4-beta across all version files (pkg/version/version.go, web/package.json)
---
## [0.4.3-beta] - 2025-01-14
### Changed
- **UI Spacing Improvements**: Reduced whitespace and padding throughout the application
- Reduced top margin from 36px to 5px on all main views (Dashboard, Profile, Performance, Workouts)
- Reduced card padding: `pa-4` → `pa-2`, `pa-3` → `pa-2`, `pa-2` → `pa-1`
- Reduced section margins: `mb-3` → `mb-2`, `mb-2` → `mb-1`
- Reduced form field spacing from `mb-2` to `mb-1`
- Removed top padding from main containers (`pt-0`)
- Changed border radius from `rounded="lg"` to `rounded` for tighter appearance
- Applied changes across Dashboard, Profile, Performance, and Workouts views
- Result: More compact, efficient use of screen space on mobile devices
## [0.4.2-beta] - 2025-01-14
### Added
- **Version and Build Display**: Added version and build number display in Profile screen
- Created new version display card at top of Profile screen
- Shows full version (e.g., "0.4.2-beta+build.1") and build number
- Backend exposes `/api/version` endpoint (public, no auth required)
- Returns `version`, `build`, `fullVersion`, and `app` fields
- Frontend fetches version info on Profile page load
- **Automatic Build Number Increment System**
- Created `scripts/increment-build.sh` for automatic build number management
- Updated `Makefile` to auto-increment build on every `make build`
- Build number stored in `pkg/version/version.go` as `Build` constant
- Added `FullVersion()`, `BuildNumber()`, and `FullString()` functions
- Format: `Major.Minor.Patch-PreRelease+build.N` (e.g., "0.4.2-beta+build.1")
### Changed
- Version endpoint moved from `/version` to `/api/version` for Vite proxy compatibility
- Updated `CLAUDE.md` with comprehensive Build Number Auto-Increment documentation
## [0.4.1-beta] - 2025-01-14
### Fixed
- **Quick Log movement search**: Fixed autocomplete not displaying movements in Quick Log dialog
- Added loading states for movements and WODs
- Added auto-select-first for better search UX
- Added search icon to match design patterns
- Added console logging for debugging data loading
- **Localhost hardcoded URLs**: Fixed profile pictures and assets not working outside of localhost
- Created `web/src/utils/url.js` with dynamic URL resolution utilities
- `getApiBaseUrl()` - Environment-aware API base URL
- `getAssetUrl()` - Converts relative paths to absolute URLs
- `getProfileImageUrl()` - Specifically handles profile image URLs
- Updated `web/src/stores/auth.js` to use new URL utilities
- Updated `web/src/views/ProfileView.vue` to use new URL utilities
- Fixed `web/src/views/VerifyEmailView.vue` to use relative URLs
- Added `/uploads` proxy to Vite dev server configuration
- **Axios configuration**: Changed baseURL to use relative URLs to leverage Vite proxy in development
### Added
- Created `web/.env.example` documenting `VITE_API_BASE_URL` environment variable
- Added comprehensive URL utility functions for production deployments
### Changed
- Quick Log dialog now opens immediately and fetches data in background for better UX
- Updated Vite proxy configuration to handle both `/api` and `/uploads` routes
## [0.4.0-beta] - 2025-01-13
### Added - Multi-Database Support
- **Multi-database support**: SQLite, PostgreSQL, and MySQL/MariaDB
- **Database migration system** with version tracking and rollback support
- Database-agnostic DSN builder
- Driver-specific schema generation (SQLite, PostgreSQL, MySQL)
- Comprehensive DATABASE_SUPPORT.md documentation
- Support for database-agnostic migrations with driver parameter
### Added - Workout Logging (Backend Complete)
- **Workout logging functionality** with complete CRUD operations
- **Movement database** with 82 standard CrossFit movements (auto-seeded)
- **Progress tracking** by movement for PR analysis
- API endpoints for workout management:
- POST /api/workouts - Create workout with movements
- GET /api/workouts - List workouts with pagination and date filtering
- GET /api/workouts/{id} - Get workout details
- PUT /api/workouts/{id} - Update workout
- DELETE /api/workouts/{id} - Delete workout (cascade deletes movements)
- GET /api/progress/movements/{movement_id} - Track performance history
- Movement management API endpoints:
- GET /api/movements - List standard movements
- GET /api/movements/search - Search movements by name
- GET /api/movements/{id} - Get movement details
- POST /api/movements - Create custom movement
### Added - Design Refinements (Planned for v0.3.0)
**Refined design decisions documented** through user consultation (not yet implemented):
**Email Verification System:**
- Optional email verification with feature unlock approach
- Users can immediately use core features without verification
- Email verification unlocks leaderboard participation and data export
- Verification email sent on registration with resend capability
- Added `email_verified` and `email_verified_at` fields to users table
**Personal Records (PR) Tracking:**
- Auto-detection system for PRs:
- Highest weight for strength movements (per user, per movement)
- Fastest time for time-based WODs (per user, per WOD)
- Most rounds+reps for AMRAP WODs (per user, per WOD)
- Manual PR flag/unflag capability for user corrections
- PR badges displayed on workout cards in dashboard and history
- PR indicators (⭐) shown in movement history lists
- Added `is_pr` field to `workout_wods` and `workout_strength` tables
**Leaderboard System with Scaled Divisions:**
- Three-division leaderboard system:
- **Rx (As Prescribed)**: Workout performed exactly as specified
- **Scaled**: Modified workout (lighter weight, fewer reps, substitute movements)
- **Beginner**: Simplified version for newer athletes
- Users self-select division when logging WOD scores
- Separate leaderboards for each division to ensure fair comparisons
- Global leaderboards for standard benchmark WODs
- Email verification required for leaderboard participation
- Added `division` field to `workout_wods` table
**Hybrid Workout Template System:**
- Users can use pre-defined WODs and admin-created templates
- Users can create and save their own custom workout templates
- "Save as Template" option when logging workouts
- Template management UI for create, edit, delete operations
- Both standard and custom content searchable and filterable
**Hybrid Movement/WOD Libraries:**
- Pre-defined library of standard CrossFit movements and WODs
- Users can add custom movements and WODs
- `is_standard` flag distinguishes pre-defined vs. user-created content
- Standard content cannot be edited by regular users
- Added `is_standard` field to `wods` and `strength_movements` tables
**Workout Scheduling:**
- Users can schedule workouts for future dates
- Calendar view distinguishes scheduled vs. completed workouts
- "Complete Scheduled Workout" flow for pre-planned training
- No push notifications initially (infrastructure ready for future)
**Performance Analytics:**
- Weight progression charts for strength movements
- Workout frequency heatmap showing consistency and streaks
- WOD leaderboards with division filters
- Focus on three primary visualizations
**Import/Export Enhancements:**
- Support for three formats: CSV, JSON, and Markdown
- CSV for spreadsheet compatibility and data analysis
- JSON for complete structured backup/restore
- Markdown for formatted workout reports
- Date range selection for partial exports
- Data type selection (Workouts, WODs, Movements, Profile)
**Data Sync Strategy:**
- "Last write wins" conflict resolution for offline sync
- Most recent timestamp takes precedence
- Suitable for single-user workout logging scenarios
- Sync status indicator for pending operations
**User Roles:**
- Simple two-tier system: regular users and admins
- First user becomes admin automatically
- No coach or gym owner roles in initial version
### Added - Database Schema Design (Planned for v0.3.0)
- **Major schema redesign** based on logical data model requirements (documented but not yet implemented)
- New `wods` table for predefined CrossFit workouts with comprehensive attributes:
- Source (CrossFit, Other Coach, Self-recorded)
- Type (Benchmark, Hero, Girl, Notables, Games, Endurance, Self-created)
- Regime (EMOM, AMRAP, Fastest Time, etc.)
- Score Type (Time, Rounds+Reps, Max Weight)
- Description, URL, and notes fields
- New `user_workouts` junction table linking users to workout instances on specific dates
- New `workout_wods` junction table linking workouts to WODs with scoring
- New `user_settings` table for user preferences (theme, notifications, export format)
- New `audit_logs` table for audit trail and accountability
- Added `updated_by` tracking to all entities for audit purposes
### Changed - Database Schema Design (Planned for v0.3.0)
- **Workouts** are now reusable templates (not user-specific instances)
- Renamed `movements` table to `strength_movements`
- Added `movement_type` to strength_movements (weightlifting, cardio, gymnastics)
- Renamed `workout_movements` to `workout_strength`
- Removed user-specific fields from workouts table (user_id, workout_date, workout_type)
- Updated ERD to reflect many-to-many relationships properly
### Changed - Multi-Database Support
- Updated migration system to accept driver parameter for database-agnostic migrations
- Improved table existence checking across all database types
- Enhanced schema creation with database-specific SQL dialects
### Migration Required (Future Work)
- Database migration from v0.1.0 to v0.3.0 will be needed when implementing v0.3.0
- See DATABASE_SCHEMA.md for planned migration steps
- Backend domain models will need updates
- API endpoints will need refactoring for new structure
### UI Updates - Dashboard Redesign
- New Dashboard UI matching design specifications
- Calendar component showing monthly workout activity
- Recent workouts cards with grouped display
- Top app bar with ActaLog logo and current date
- Unified bottom navigation across all authenticated views
- Avatar support for user profile icon
- Workout badge for Personal Records (PRs)
- Complete Dashboard redesign with calendar view
- Moved header and bottom navigation to App.vue for consistency
- Updated color scheme to match brand guidelines
- Improved mobile-first responsive design
- Enhanced bottom navigation with better iconography
### Documentation
- **Reorganized app navigation structure** - Settings Menu as central hub
- Added comprehensive "Screens & Navigation Flow" section to REQUIREMENTS.md
- **33 core screens** defined with routes, purposes, and components
- Settings Menu flyout accessed from user avatar
- Management screens for WODs, Strength Movements, and Workout Templates with full CRUD operations
- Import/Export data screens
- App Preferences screen
- Navigation flow diagrams
- Screen interaction patterns
- PWA-specific screens (install prompt, offline indicator)
- Added `birthday` field to User profile
### Planned
- Implement database migration scripts for v0.3.0 schema
- Update backend domain models for new schema
- Seed data for standard WODs and movements
- Connect frontend to workout logging APIs
- Workout templates and named WOD database
- Charts and graphs for progress visualization
- Push notifications for workout reminders
- Web Share API integration
- Implement all 33 screens defined in screen inventory:
- Management screens for WODs (List, Create, Edit with CRUD operations)
- Management screens for Strength Movements (List, Create, Edit with CRUD operations)
- Management screens for Workout Templates (List, Create, Edit with CRUD operations)
- Import/Export data screens
- Settings Menu flyout implementation
- First-user-as-admin logic
- Configurable registration control (ALLOW_REGISTRATION)
- SQLite database with auto-initialization
- PostgreSQL and MariaDB support
- Database schema with users, workouts, movements, and workout_movements tables
- Bcrypt password hashing (cost factor 12)
- CORS middleware with configurable origins
- Request logging middleware
- Health check endpoint (`/health`)
- Version endpoint (`/version`)
- Docker and docker-compose configuration
- Makefile for development workflow
- Windows batch script (`build.bat`) for Windows users
- Comprehensive documentation:
- README.md with quick start guide
- ARCHITECTURE.md with Clean Architecture patterns
- DATABASE_SCHEMA.md with ERD diagrams
- SETUP.md for local and Docker development
- REQUIREMENTS.md with user stories
- AI_INSTRUCTIONS.md for development guidelines
- Frontend views:
- Login and registration pages
- Dashboard with bottom navigation
- Workout logging form (matching design)
- Workouts history view
- Performance tracking view
- Profile and settings views
- 404 error page
- Vue Router with authentication guards
- Pinia state management for auth
- Axios HTTP client with interceptors
- Custom ActaLog theme with design colors
- Mobile-first responsive design
- ESLint 9 with flat config format
- Prettier code formatting
- golangci-lint configuration
- Version management system (v0.1.0-alpha)
### Fixed
- Windows build permission issues (uses project-local cache)
- SQLite driver name corrected from 'sqlite' to 'sqlite3'
- npm dependency deprecation warnings
- esbuild security vulnerability
- ESLint 8 to ESLint 9 migration
- CORS configuration for development
### Security
- JWT token generation and validation
- Password hashing with bcrypt
- SQL injection prevention via parameterized queries
- CORS origin whitelisting
- Secure defaults in configuration
- No sensitive data in error responses
### Changed
- Updated all npm dependencies to latest versions
- Migrated from ESLint 8 to ESLint 9
- Updated Vite to version 6
- Updated Vue.js to version 3.5
- Updated Vuetify to version 3.7
### Developer Experience
- Hot reload support for frontend (Vite)
- Clean build artifacts with `make clean`
- Formatted code with `make fmt`
- Linting with `make lint`
- Testing support with `make test`
- Docker support for easy deployment
- Cross-platform build scripts (Makefile + build.bat)
## [0.3.1-beta] - 2025-11-10
### Added
- **Email Verification System (Complete)**
- Database migration v0.3.1 adding `email_verified` and `email_verified_at` columns to users table
- Backend API endpoints: `GET /api/auth/verify-email`, `POST /api/auth/resend-verification`
- Email service with SMTP integration for sending verification emails
- Styled HTML email templates with verification links
- 24-hour token expiration with secure token generation (crypto/rand)
- Single-use verification tokens (marked as used after verification)
- Repository layer: `CreateVerificationToken()`, `GetVerificationToken()`, `MarkTokenAsUsed()`
- Service layer: `SendVerificationEmail()`, `VerifyEmailWithToken()`, `ResendVerificationEmail()`
- Handler layer: `VerifyEmail()`, `ResendVerification()` with proper error handling
- **Email Verification Frontend**
- VerifyEmailView component at `/verify-email?token=...` route
- Automatic email verification on page load
- Loading, success, and error states with appropriate messaging
- Handles expired, invalid, and already-used tokens
- Updates auth store user object on successful verification
- ResendVerificationView component at `/resend-verification` route
- Email input form to request new verification email
- Success confirmation displaying the email address
- Comprehensive error handling (404, 400, network errors)
- Updated RegisterView to show verification success message
- No longer auto-redirects to dashboard after registration
- Displays sent email address and 24-hour expiration notice
- Link to resend verification if email not received
- Dashboard verification status banner
- Warning alert for users with unverified emails
- Prominent "Resend Email" button
- Closable alert for better UX
### Changed
- User registration flow now includes email verification step
- Users receive verification email immediately after registration
- Dashboard shows verification reminder until email is verified
- Router updated with `/verify-email` and `/resend-verification` routes
- Navigation guards allow verify-email access for both authenticated and unauthenticated users
- Version bumped to 0.3.1-beta across all version files
### Technical
- Email verification tokens stored in `email_verification_tokens` table
- Tokens generated using crypto/rand (32 bytes hex-encoded) for security
- SMTP configuration via environment variables (EMAIL_FROM, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS)
- HTML email template with inline styles for cross-client compatibility
- Authorization checks ensure users can only resend verification for their own email
- Frontend build: 618 modules, 47 PWA cache entries
- Multi-database support (SQLite, PostgreSQL, MySQL) for email_verified field
## [0.3.0-beta] - 2025-11-10
### Added
- **Personal Records (PR) Tracking System**
- Automatic PR detection when logging workouts (weight-based comparison)
- Manual PR flag toggle via API endpoint
- Database migration v0.3.0 adding `is_pr` column to workout_movements
- Multi-database support (SQLite, PostgreSQL, MySQL) for PR field
- New domain models: `PersonalRecord` struct and `IsPR` field in `WorkoutMovement`
- Repository methods: `GetPersonalRecords()`, `GetMaxWeightForMovement()`, `GetPRMovements()`
- Service layer methods: `DetectAndFlagPRs()`, `GetPersonalRecords()`, `TogglePRFlag()`
- API endpoints: `GET /api/workouts/prs`, `GET /api/workouts/pr-movements`, `POST /api/workouts/movements/:id/toggle-pr`
- Gold trophy badges (mdi-trophy) on workout cards containing PRs
- Individual PR indicators next to movements in workout lists
- Dedicated PR History page at `/prs` route showing recent PRs and all-time records
- Visual distinction with gold/amber color scheme (#ffc107) for PR indicators
- **Password Reset Frontend (Part 3/3)**
- Forgot Password view with email submission form
- Reset Password view with token validation and new password form
- Router configuration for `/forgot-password` and `/reset-password/:token` routes
- "Forgot password?" link added to Login view
- Integration with backend password reset API endpoints
- Success/error messaging for user feedback
### Changed
- Integrated PR detection into workout creation workflow
- Updated RecentWorkoutsCards component to display PR badges
- Updated WorkoutsView to show PR indicators on individual movements
- Enhanced router with authentication guards for password reset routes
- Version bumped to 0.3.0-beta across all version files
### Technical
- PR auto-detection algorithm: compares current weight against previous max for each movement
- Authorization checks on PR flag toggle to ensure workout ownership
- Backward-compatible database migration with DEFAULT values
- Clean Architecture maintained: domain → repository → service → handler layers
- All PR queries include proper user scoping for security
## [0.2.0-beta] - 2025-11-06
### Added
- Complete workout CRUD functionality with RESTful API endpoints
- Workout repository layer for database operations
- Movement repository with 31 seeded standard CrossFit movements
- Workout movement repository for linking movements to workouts
- Workout service layer with business logic and authorization
- JWT authentication middleware for protected routes
- Dashboard with real-time workout statistics (total workouts, monthly count)
- Recent workouts display on dashboard (last 5 workouts)
- Workout saving functionality from Log Workout screen
- Workouts list view with movement details
- Autocomplete/search functionality for movement selection
- Custom movement item templates showing type and icons
- Modern UI design with cyan accent color (#00bcd4)
- Dark navy header (#2c3e50) across all views
- Responsive scrolling with fixed header and footer navigation
### Changed
- Updated LogWorkoutView with functional save button and API integration
- Updated WorkoutsView to fetch and display real workout data
- Updated DashboardView to show live statistics from API
- Updated PerformanceView with searchable movement dropdown
- Improved font readability with darker colors (#1a1a1a)
- Reduced vertical spacing for better mobile fit
- Changed v-select components to v-autocomplete for better UX
- Enhanced workout responses to include full movement details
### Fixed
- Cache directory creation issue in Makefile (mkdir -p added to run/dev targets)
- SQLite driver name changed from "sqlite" to "sqlite3" in config
- Workout save button now properly calls API endpoint
- Vertical scrolling enabled on all views
- Content no longer runs off bottom of screen
- Movement names now display correctly in workout lists
### Technical
- Implemented Clean Architecture pattern (domain → repository → service → handler)
- Added dependency injection for repositories and services
- Integrated JWT token validation in middleware
- Database seeding for standard movements on first run
- Proper error handling and validation in API endpoints
- User authorization checks in workout service layer
## [0.1.0-alpha] - 2025-11-05
### Added
- Initial project setup with Go backend and Vue.js frontend
- User authentication with JWT tokens
- Basic user registration and login endpoints
- Database schema for users, workouts, movements, and workout_movements
- SQLite and PostgreSQL database support
- Vue.js frontend with Vuetify 3 UI framework
- Vue Router setup with authentication guards
- Pinia store for state management
- Basic view scaffolding (Dashboard, Performance, Workouts, Profile, Login, Register)
- Bottom navigation with mobile-first design
- Clean Architecture folder structure
- Configuration management with environment variables
- Makefile for common development tasks
- Documentation (README.md, ARCHITECTURE.md, AI_INSTRUCTIONS.md, DATABASE_SCHEMA.md)
### Technical
- Go 1.24+ with Chi router
- Vue 3 with Composition API
- Vuetify 3 for UI components
- Axios for HTTP requests
- bcrypt for password hashing
- JWT for authentication
- SQLite3 driver integration
---
## Version History Format
### [Version] - YYYY-MM-DD
#### Added
New features that have been added to the project.
#### Changed
Changes in existing functionality.
#### Deprecated
Soon-to-be removed features.
#### Removed
Features that have been removed.
#### Fixed
Bug fixes.
#### Security
Security-related changes or fixes.
---
**Current Version:** 1.2.1
**Schema Version:** v0.32.0 (Coach Role & Role Rename)
**Last Updated:** 2026-04-01