Skip to content

fix: product readiness — security, performance, observability, UX#8

Open
rcasto123 wants to merge 9 commits into
mainfrom
fix/product-readiness-fixes
Open

fix: product readiness — security, performance, observability, UX#8
rcasto123 wants to merge 9 commits into
mainfrom
fix/product-readiness-fixes

Conversation

@rcasto123

Copy link
Copy Markdown
Owner

Summary

8 targeted fixes identified from a product readiness review, covering security hardening, performance, observability, and UX polish.

Security

  • Harden credentials auth (auth.ts): Existing users without a passwordHash can no longer log in via the admin master key — requires an explicit stored hash. Bootstrap (first-time admin creation via ADMIN_PASSWORD) now self-disables once any admin exists, and the bootstrapped account gets its password properly hashed so subsequent logins work.

Stability / Correctness

  • Env validation at startup (lib/env.ts): Zod schema validates DATABASE_URL, NEXTAUTH_SECRET, ALLOWED_EMAIL_DOMAIN at import time — misconfigured deployments fail fast with a clear message.
  • Magic numbers extracted (lib/constants.ts): Risk thresholds, stale-access windows, upload limits, and invite expiry are now named constants used consistently across 5 files.
  • 10 MB upload limit (app/api/spend/upload): Returns HTTP 413 before reading file content — prevents OOM on large uploads.

Performance

  • Eliminate N+1 in Google/M365 sync (worker/jobs/sync-google.ts, sync-m365.ts): App and user DB lookups are now batched with findMany + Map before the grant loop. Previously: O(N×M) round-trips per sync; now: 2 bulk queries + O(N) upserts.

Observability

  • Structured pino logging (lib/logger.ts): Replaced all console.log/error in worker jobs and alert module with pino child loggers. Each log line includes { module, ...context } for filtering in Railway log drains.
  • /api/health endpoint: DB ping (SELECT 1) returns 200 ok or 503 error — ready for Railway health check probe configuration.

UX

  • Actionable alerts: Offboarding-risk and stale-access alerts now show a Revoke Access button (PATCH /api/access/revoke) that deactivates the grant and auto-resolves the alert.
  • Actionable empty states: Inventory, Access, Discovery, and Spend pages all show meaningful empty states with "Set up a connector →" links instead of bare "No data" text.

Test Plan

  • Login with credentials: existing user with no passwordHash is rejected
  • Bootstrap: works when zero admins exist; blocked when ≥1 admin exists
  • GET /api/health returns { status: "ok", db: "ok" } with DB connected
  • Upload a CSV > 10 MB → expect 413 response
  • Trigger a Google Workspace sync and verify connector logs show structured JSON
  • Open Alerts page with no data → "No active alerts" empty state shown
  • Open Inventory with no apps → "Set up a connector →" link shown

🤖 Generated with Claude Code

rcasto123 and others added 9 commits April 21, 2026 16:27
…tstrap only when zero admins

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er lookups

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all console.log/error calls with a pino-backed createLogger helper
across the worker entrypoint, all five sync jobs, and lib/alerts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant