Commit Graph

66 Commits

Author SHA1 Message Date
igm
b085a315be Add transactions to auth procedures and extract DB models
- Wrap multiple DB operations in transactions for atomicity:
  - login-if-completed: device upsert + session + login_request deletion
  - forgot-password: delete old tokens + insert new token
  - signup: session + email_verification creation

- Extract reusable DB model operations to packages/db/src/models/:
  - sessions.ts: insertSession()
  - user-devices.ts: upsertUserDevice(), isDeviceTrusted()

- Update session.ts to use new model functions from @reviq/db
- Fix type narrowing in admin.test.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:52:05 +08:00
igm
16f827e8f0 Merge branch 'test-coverage'
Some checks failed
CI / ci (push) Has been cancelled
Add test utilities and ast-grep rules for code quality.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:05:07 +08:00
igm
947c73dbdc Remove unnecessary exclude from tsconfig files
TypeScript excludes node_modules by default, and dist is handled
by outDir or include patterns.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:02:55 +08:00
igm
2baf10b0cd Replace String() calls with .toString()/.toLocaleString() per ast-grep rule
- Add formatError() helper in CLI to safely handle unknown error types
- Add uniqueTestId() helper for generating unique test identifiers
- Replace String(id) with id.toString() for database ID conversions
- Replace String(n) with n.toLocaleString() for user-facing number formatting
- Fix TypeScript errors in test files (undefined checks, unused variables)
- Update lint commands to include ast-grep scanning

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:02:46 +08:00
igm
26d10d452f Rename @reviq/utils to @reviq/server-utils and add package READMEs
- Rename packages/utils/ to packages/server-utils/
- Update all imports and package.json references
- Add READMEs for frontend-utils, server-utils, and common packages
- Update main README with new package structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:57:28 +08:00
igm
6fa4da1abb Fix lint errors and add ast-grep rule for countAll
- Fix template literal expressions: wrap Date.now() in String()
- Add missing afterAll import in admin.test.ts
- Fix countOwners to use countAll() without misleading <number> type
- Add ast-grep rule to prevent countAll<number>() usage
- Fix formatting issues from merge conflict resolution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:40:06 +08:00
igm
92f7e1df09 Merge origin/master and migrate tests to describeE2E
- Resolve merge conflicts in auth.test.ts, me.test.ts, db/schema.sql
- Merge new loginRequestMiddleware tests into auth.test.ts describeE2E wrapper
- Merge new authMiddleware tests into me.test.ts describeE2E wrapper
- Add me.apiTokens and me.invites tests in separate describeE2E block
- Migrate admin.test.ts to use describeE2E and @reviq/test-helpers
- Migrate orgs.test.ts to use describeE2E and @reviq/test-helpers

All e2e tests now properly use the describeE2E helper which enables
SKIP_DB_TESTS environment variable support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:19:29 +08:00
igm
b2fba6e150 Add test infrastructure with coverage and DB test skipping
- Create @reviq/test-helpers package with shared test utilities
- Add describeE2E helper that auto-prefixes test names with [e2e]
- Support SKIP_DB_TESTS=1 to skip database-dependent tests
- Add unix socket support for TEST_DATABASE_URL
- Add root commands: test:unit, test:all, test:cov, test:unit:cov
- Configure bunfig.toml to exclude dist/ from coverage reports
- Clean up tsconfig.json files to remove redundant settings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:03:41 +08:00
igm
ebc85af62c Add comprehensive e2e tests for API procedures with 100% coverage
- Add admin.test.ts: Tests for superuser operations (users, orgs, sites)
- Add orgs.test.ts: Tests for org management, members, invites, sites
- Expand me.test.ts: Add API tokens, invites, authMiddleware error paths
- Expand auth.test.ts: Add loginRequestMiddleware tests, weak password test fix

Bug fixes:
- Fix countOwners() in orgs/helpers.ts to convert PostgreSQL bigint to number
- Fix signup race condition by handling unique constraint violations gracefully

All 283 tests pass with 100% function coverage on procedures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 12:53:19 +08:00
igm
730021a5ea Merge branch 'parallelize-tests-better' 2026-01-10 19:17:50 +08:00
igm
dcb48a5d5e Migrate e2e tests to transaction-based isolation
Replace table truncation with transaction rollback for test isolation.
Each test now runs in a transaction that auto-rolls back, improving
test performance and isolation. Tests that call procedures with internal
transactions use getSharedDb() directly with appropriate comments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 19:16:47 +08:00
igm
8f3a1f2962 Merge origin/master into reviq-auth-login-command
Resolved conflicts:
- apps/api-server/src/router.ts: Use meRoutes from master
- packages/api-contract/src/contract.ts: Keep master's nested sessions/devices/invites structure, add apiTokens

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 19:03:37 +08:00
igm
a7d6beaf5a Add API token management for CLI authentication
- Add reviq auth login --token <token> command for CLI authentication
- Create /account/api-tokens page for token management (superuser only)
- Add me.apiTokens endpoints (list, create, delete)
- Require superuser status and trusted session for token creation
- Show API Tokens nav link only for superusers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:58:27 +08:00
RevIQ
cca901a9b9 Merge branch 'whats-left' 2026-01-10 18:10:40 +08:00
RevIQ
42badf3c52 Add DBIP city database and improve geo test coverage
- Add dbip-city-lite package to devenv for GeoIP testing
- Set GEOIP_DATABASE_PATH env var to point to the MMDB database
- Add tests for initGeoReader double-init and error handling
- Add real database tests for IP lookups (US, AU, DE, GB)
- Make real database tests conditional with describe.skipIf
- Improve test coverage from ~97% to 98.82%

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:10:30 +08:00
RevIQ
319edf70db Fix IP address not being set on sessions from localhost
The extractClientIP() function only checked proxy headers (X-Forwarded-For,
CF-Connecting-IP, etc.) which don't exist when running locally without a proxy.

Changes:
- Add clientIP field to APIContext
- Use Bun's server.requestIP() to get client IP from direct socket connection
- Update getGeoInfo() to accept fallback IP parameter
- Pass context.clientIP to getGeoInfo() in auth procedures

Now sessions will have IP address set even for local development (::1 or 127.0.0.1).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:08:21 +08:00
RevIQ
74b26818ca Add comprehensive e2e tests for all auth procedures
Tests cover all login scenarios from docs/initial-app.md:
- Signup with password and passkey
- Password login with trusted device (immediate completion)
- Password login with untrusted device (email confirmation)
- Full passkey authentication flow
- User with no auth methods (stays pending)
- Non-existent email (anti-enumeration with fake token)
- Email verification and resend flows
- Password reset with session revocation
- Logout

All auth procedures now have 100% function coverage.
127 tests passing across 3 e2e test files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 17:55:39 +08:00
RevIQ
40df74fdd0 Merge branch 'testing-improvements'
Combines testing improvements with org invites feature:
- Sessions and devices now use subrouter structure (me.sessions.*, me.devices.*)
- Added me.invites subrouter for org invitations
- Updated test scripts to include coverage and unit tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 17:24:51 +08:00
RevIQ
d824cf4116 Refactor API to use nested sessions/devices routers and fix test infrastructure
- Update API contract to use nested router structure for sessions and devices
  (me.sessions.list, me.devices.getInfo, etc.)
- Update frontend Svelte components to use new nested API paths
- Fix test assertion patterns for consistency (remove async () => wrappers)
- Fix test-db.ts findRepoRoot to use existsSync for directory checking
  (Bun.file().exists() returns false for directories)
- Add ESLint config override for test files to handle expect().rejects patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 17:17:50 +08:00
RevIQ
39863bd947 Add org invites section to dashboard with accept/decline flow
Backend:
- Add me.invites endpoints (list, get, accept, decline) to API contract
- Create invites procedures for fetching user's pending invites
- Only show invites if email matches and is verified
- Refactor me routes into me/_routes.ts for consistency

Frontend:
- Add pending invitations section to /dashboard page
- Create /account/org-invites/[inviteId] page for accept/decline
- Show invite details (org, role, inviter, dates)
- Redirect to org dashboard after accepting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 17:11:22 +08:00
RevIQ
55b312a03d Merge branch 'geo-fix' 2026-01-10 16:42:07 +08:00
RevIQ
575ca83300 Add GeoIP lookup support and update device fingerprints to base58
- Add maxmind library for GeoIP database lookups when not behind Cloudflare
- Extract client IP from multiple header sources (CF, X-Real-IP, X-Forwarded-For, etc.)
- Change device fingerprints from UUID to base58 with device_ prefix
- Add isValidDeviceFingerprint() that accepts both new and legacy formats
- Colocate unit tests with source files, remove __tests__/unit directory
- Add test coverage reporting to test script

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 16:41:21 +08:00
RevIQ
1bf05465c3 Replace void returns with { success: true } across all API endpoints
- Add successResponseSchema to common.ts for explicit success responses
- Update all auth, me, orgs, and admin procedures to return { success: true }
- Update contract.ts to use successResponseSchema instead of z.void()
- Add ast-grep rule to prevent future z.void() usage in contracts
- Add build:packages script to root package.json
- Fix test file lint errors with eslint-disable comments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 16:30:22 +08:00
RevIQ
3b502d9319 Add comprehensive e2e tests for sessions and devices with 100% coverage
- Add tests for me.sessions (list, revoke, revokeAll)
- Add tests for me.devices (getInfo, trust, listTrusted, untrust, revokeAll)
- Refactor router to use nested namespaces (me.sessions.*, me.devices.*)
- Add createUserAPIContext helper that returns { context, token }
- Add createDevice helper for device tests
- Update createSession to return { token, sessionId }

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 16:23:36 +08:00
RevIQ
6b43910238 Add test coverage and fix webauthn e2e tests to use real sessions
- Add test:e2e:coverage script with Bun's built-in coverage support
- Create bunfig.toml with coverage configuration (text + lcov reporters)
- Fix webauthn tests to create real database sessions/login requests
  instead of mock context objects that bypass auth middleware
- Add createUserAPIContext helper for cleaner test code
- Update security tests to expect NOT_FOUND when accessing other
  user's passkeys

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 16:14:24 +08:00
RevIQ
44656b1bd1 Merge branch 'testing-improvements' 2026-01-10 15:45:56 +08:00
RevIQ
dd7b2ea8e4 Revamp navigation with org switcher and user menu, fix passkey login
Navigation changes:
- Add org-switcher dropdown to sidebar showing user's orgs
- Add user-menu dropdown with account settings and sign out
- Make nav items dynamic based on org context
- Move performance page to /dashboard/[slug]/performance
- Add reports placeholder page at /dashboard/[slug]/reports
- Remove admin link from sidebar (separate layout)
- Update mobile nav to match sidebar changes
- Install shadcn dropdown-menu and popover components

Auth fix:
- Mark login request as completed after passkey verification
- Previously passkey auth didn't complete the login flow, requiring
  unnecessary email verification

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 15:45:03 +08:00
RevIQ
72fa560648 Merge branch 'master' into testing-improvements 2026-01-10 15:31:37 +08:00
RevIQ
2c2556a5ea Add e2e tests for me.* procedures and fix code review issues
- Add comprehensive e2e tests for me.get, me.authStatus, me.setupProfile,
  me.updateProfile, me.setPassword, and me.delete (21 tests)
- Make createDb require explicit connection string (no default env lookup)
- Add database name validation to prevent SQL injection in CREATE DATABASE
- Fix getTestDatabaseUrl to throw instead of returning empty string
- Replace brittle relative path with findRepoRoot() function
- Extract magic numbers (SESSION_EXPIRY_MS, API_TOKEN_EXPIRY_MS, ONE_DAY_MS)
- Consolidate duplicate createAPIContext functions
- Add hasPassword field to meAuthStatus and toUserResponse

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 15:24:42 +08:00
RevIQ
ddd7c0c03b Add generateSecureBase58Token to shared utils with login_ prefix
- Create packages/utils/src/generate-base58-token.ts with typed prefix support
- Function returns `${TPrefix}${string}` for type-safe prefixed tokens
- Add isBase58() validator and parseBase58Token() helper
- Add comprehensive tests (13 test cases)

- Update login request tokens to use "login_" prefix
- Fix login-password.ts to not replace token (cookie/DB mismatch bug)
- Migrate all token generation from generateSecureToken (hex) to
  generateSecureBase58Token (base58)
- Remove duplicate token generation from api-server/utils/crypto.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 19:31:30 +08:00
RevIQ
68fc67ba4a Fix lint errors and formatting
- Wrap error.code with String() in CLI complete-login command
- Apply formatting fixes from linter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 19:16:23 +08:00
RevIQ
d66894e8dc Add admin CLI command and auth guard, use oRPC client
CLI changes:
- Use official oRPC client instead of manual HTTP requests
- Add admin complete-login command for dev workflow
- Remove type assertions, use proper ContractRouterClient typing
- Add @orpc/client and @orpc/contract dependencies

API changes:
- Use oRPC cookie helpers from @orpc/server/helpers
- Improve admin complete-login error messages (expired, already completed)

Dashboard changes:
- Add AuthGuard component to redirect unauthenticated users to /auth/login
- Update confirm page with correct CLI command and copy button
- Remove duplicate auth redirect from dashboard layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 19:12:19 +08:00
RevIQ
0e6a028ef0 Add missing hasPassword property to user responses
The API contract requires hasPassword in user responses but it was
missing from toUserResponse helper and meAuthStatus handler.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 18:31:31 +08:00
RevIQ
d4e20d41b5 Merge branch 'cli-improvements-1' with @reviq/utils password hashing
- Use executeBootstrap helper from @reviq/db for CLI bootstrap
- Update @reviq/db to use @reviq/utils for PBKDF2-SHA256 password hashing
  (Cloudflare Workers compatible)
- Keep @scure/base for base58 token encoding
- Remove redundant password.ts from @reviq/db (import directly from @reviq/utils)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 18:17:45 +08:00
RevIQ
cfe26c8820 Merge branch 'workstream-i' 2026-01-09 18:12:45 +08:00
RevIQ
c1afc39062 Add utils package with Web Crypto password hashing
- Create @reviq/utils package with PBKDF2-SHA256 password hashing
  compatible with Cloudflare Workers (uses crypto.subtle)
- Update api-server and CLI to use new utils package for consistent
  password hashing format across the codebase
- Add pino logging to api-server for better request debugging
- Make login request tokens cryptographically secure base58 strings
  instead of database IDs
- Add migration to make login_requests.token non-nullable with unique
  constraint
- Fix RPCLink URL construction for client-side API calls
- Add db:codegen script to root package.json

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 18:12:33 +08:00
RevIQ
1083cde9b7 Implement Workstream I: Account pages with code review fixes
Add account management UI with profile settings, authentication options,
device/passkey management, and session management pages.

Key changes:
- Add account pages: profile, auth, devices, sessions
- Add dialog components: confirm, add-passkey, change-password, rename-passkey
- Return passkeyId from verifyRegistration to fix race condition
- Add hasPassword field to user schema
- Add aria-label to dialog close button for accessibility
- Add avatar URL validation and fix phone input styling
- Add comprehensive test plan documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 18:04:02 +08:00
RevIQ
77e6754427 Merge branch 'master' into cli-improvements-1 2026-01-09 18:02:46 +08:00
RevIQ
6b9b04d1d0 Improve API token format and enhance auth status command
- Change token format to reviq_<base58> prefix instead of raw hex
- Add me.authStatus API endpoint for detailed auth information
- Enhance CLI `reviq auth status` to show token details from API
- Add comprehensive tests for token generation (18 tests)
- Extract bootstrap logic to @reviq/db for reusability and testing
- Remove default db export; callers must use createDb() directly

Token changes:
- New format: reviq_<base58-encoded-32-bytes>
- Added parseToken() for validation
- Added isValidTokenFormat() helper

Auth status endpoint returns:
- User profile information
- Auth method (api_token or session)
- Token/session details (name, expiration, last used)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:59:02 +08:00
RevIQ
ed2636ab83 Use nullish coalescing in admin users create
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:14:21 +08:00
RevIQ
d04f061120 Fix lint and formatting issues
- Sort imports alphabetically in helpers.ts and router.ts
- Simplify boolean comparison in users/update.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:13:14 +08:00
RevIQ
9158146462 Build api-server and cli as compiled binaries
- Rename api-server package to @reviq/api-server
- Add --compile flag to bun build for both packages
- Output standalone executables instead of bundled JS

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:11:20 +08:00
RevIQ
c4b0509023 Implement shell completions for CLI (Workstream N-Completions)
Add `reviq completions bash/zsh` command with dynamic shell completions:

- Create bash-complete.ts entry point using stricli's proposeCompletions API
- Add completions command with bash and zsh support (fish planned)
- Extract app export to separate app.ts for shared imports
- Add @stricli/auto-complete dependency and __reviq_bash_complete bin entry

Also fix lint/type errors in api-server tests and helpers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:11:10 +08:00
RevIQ
786232e1b1 Merge branch 'workstream-k' 2026-01-09 17:03:07 +08:00
RevIQ
3eb44adf05 Fix TypeScript and lint errors
- Add missing reqHeaders/resHeaders to webauthn test context
- Fix Session.id type from number to string in test
- Remove unnecessary Number() conversion in countOwners

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:01:22 +08:00
RevIQ
c0966365f3 Refactor admin procedures into separate files
Extract admin procedures from router.ts into dedicated files under
procedures/admin/ with consolidated exports via _routes.ts. Adds shared
helper functions for response transformation and includes race condition
fixes via transaction-scoped existence checks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:00:04 +08:00
RevIQ
2655c57b9e Merge branch 'wt3': WebAuthn enhancements and virtual authenticator
- Enhanced createRegistrationOptions to look up existing users
- Added virtual-authenticator testing package
- Added WebAuthn e2e and unit tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 16:55:14 +08:00
RevIQ
9cf95095c3 Implement Workstream J: Org procedures (backend)
Add complete organization management procedures:
- orgs.list, create, get, update, delete, leave
- orgs.members.list, updateRole, remove
- orgs.invites.list, create, cancel, accept
- orgs.sites.list

Key features:
- Role-based access control (owner > admin > member)
- Transaction-protected owner count checks to prevent race conditions
- Privilege escalation prevention (only owners can invite owners)
- Graceful constraint violation handling with friendly error messages
- Email sending for org invitations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 16:50:29 +08:00
RevIQ
bd9be3e441 Add comprehensive WebAuthn e2e/unit tests and virtual authenticator package
- Create @reviq/virtual-authenticator package with cryptographically valid
  WebAuthn credential generation for testing
- Add e2e tests for WebAuthn registration, authentication, passkey management
- Add unit tests for passkey-helpers and VirtualAuthenticator
- Add security tests for counter replay and tampered responses
- Configure test database environment in devenv.nix
- Add turbo.json test tasks and workspace configuration

Test results: 98 tests passing (54 virtual-authenticator, 25 e2e, 19 unit)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 16:46:02 +08:00
RevIQ
2d445cc47b Merge branch 'workstream-f1' 2026-01-09 16:31:58 +08:00