Audit Log Filtering Implementation Plan
Audit Log Filtering Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add server-side audit-log filtering to the management console.
Architecture: The API decodes and validates query filters, the audit repository pushes every filter into SQL, and pagination continues to use the existing (createdAt, id) keyset. The Vue page keeps a filter form above the table and explicitly applies filters with the query action.
Tech Stack: Go net/http, SQL repositories, Vue 3, TypeScript, Element Plus, Vitest.
Spec: docs/superpowers/specs/2026-09-09-token-scope-route-edit-audit-design.md
Global Constraints
- Filtering happens server-side so older pages are not incorrectly excluded.
- Text filters are exact matches after trimming.
createdFromandcreatedToare inclusive and use RFC3339 date-time values.createdFrommust not be later thancreatedTo.- Ordering remains
created_at DESC, id DESC. - The API is admin-only and returns no secrets.
- No database schema change is required.
Task 1: Repository filter contract
Files:
- Test:
internal/storage/audit_repository_test.go - Modify:
internal/storage/models.go - Modify:
internal/storage/repository.go
Interfaces:
- Produces:
AuditFilter{ActorUserID, Action, ResourceType, ResourceID string; CreatedFrom, CreatedTo *time.Time}. -
Produces:
AuditRepository.List(ctx context.Context, filter AuditFilter, cursor string, limit int) (Page[AuditLog], error). -
Step 1: Write the failing repository test.
TestAuditRepositoryAppliesFiltersWithStablePaginationcreates target and noise records, applies all five filters, and asserts the first filtered page and its cursor. -
Step 2: Verify the red phase.
go test ./internal/storage -run TestAuditRepositoryAppliesFiltersWithStablePagination -count=1Actual failure before implementation:
AuditFilteris undefined andListdoes not accept a filter argument. -
Step 3: Implement SQL filter pushdown.
Add exact-match conditions for actor, action, resource type, and resource ID; add inclusive
created_atbounds; combine them with the keyset cursor condition and retaincreated_at DESC, id DESC. -
Step 4: Verify the focused repository test.
go test ./internal/storage -run TestAuditRepositoryAppliesFiltersWithStablePagination -count=1Expected result: PASS.
Task 2: API query contract
Files:
- Test:
internal/server/api_test.go - Modify:
internal/server/api.go - Modify:
docs/api/openapi.yaml
Interfaces:
- Consumes:
AuditFilterandAuditRepository.List. -
Produces:
GET /api/v1/audit-logsquery parametersactorUserId,action,resourceType,resourceId,createdFrom, andcreatedTo. -
Step 1: Write the failing API test.
TestAuditAPIFiltersAndValidatesTimeRangeverifies that all filters exclude noise, invalid timestamps return400, and a reversed time range returns400. -
Step 2: Verify the red phase.
go test ./internal/server -run TestAuditAPIFiltersAndValidatesTimeRange -count=1Actual failure before implementation: all records are returned because query filters are ignored.
-
Step 3: Implement query decoding and validation.
Trim text filters, parse both time filters with
time.RFC3339Nano, reject parse errors and reversed ranges, then pass the filter to the service. -
Step 4: Update OpenAPI.
Document all six optional filters, inclusive time semantics, exact text matching, and the
400response. -
Step 5: Verify the focused API test.
go test ./internal/server -run TestAuditAPIFiltersAndValidatesTimeRange -count=1Expected result: PASS.
Task 3: Management UI filter form
Files:
- Test:
web/src/tests/audit-logs.spec.ts - Modify:
web/src/api/client.ts - Modify:
web/src/views/AuditLogs.vue - Modify:
web/src/i18n/messages/zh-CN.ts - Modify:
web/src/i18n/messages/en-US.ts
Interfaces:
- Produces:
AuditLogFilterandlistAuditLogs(params: AuditLogFilter & { cursor?: string; limit?: number }). -
Produces:
auditFilter(),search(), andresetFilters()inAuditLogs.vue. -
Step 1: Write the failing frontend tests.
Assert that all filter values are serialized into the API URL and that the view contains the filter card, datetime range, reset/query actions, and right-aligned action area.
-
Step 2: Verify the red phase.
npm --prefix web test -- --run src/tests/audit-logs.spec.tsActual failures before implementation: the API URL only contained
limit=100, and the view had no filter card. -
Step 3: Implement the typed API client.
Add
AuditLogFilter, serialize only non-empty values, and preserve cursor and limit parameters. -
Step 4: Implement the filter form.
Add a light card above the table with a datetime range and four exact-match inputs. Place Reset and Search at the bottom right; Search reloads from the first page and Reset clears all fields before reloading.
-
Step 5: Verify the focused frontend test.
npm --prefix web test -- --run src/tests/audit-logs.spec.tsExpected result: PASS.
Task 4: Full verification
Files:
- Modify:
docs/user-guide/server-admin.md - Modify:
docs/pull-requests/2026-09-09-token-scope-route-edit-audit.md -
Modify:
internal/server/web_dist/after building web assets. -
Step 1: Update user and PR documentation.
Document the five filters, query/reset behavior, server-side semantics, and inclusive time range.
-
Step 2: Run full Go verification.
go test ./... -count=1 go test -race ./... go vet ./... git diff --checkExpected result: all commands pass.
-
Step 3: Run full frontend verification and refresh embedded assets.
npm --prefix web test -- --run npm --prefix web run build rsync -a --delete web/dist/ internal/server/web_dist/ ./scripts/verify-web-embed.shExpected result: all frontend tests and the production build pass, and embedded assets verify.
Completion Record
This is a retroactive plan for a bounded feature approved and completed in this session. The red-phase failures above are the actual observed results. No commit, push, merge, or remote PR was performed.