Skip to content

feat: add dashboard with github auth, onboarding and runs list#113

Merged
AKogut merged 2 commits into
mainfrom
feat/dashboard-foundation
Jul 21, 2026
Merged

feat: add dashboard with github auth, onboarding and runs list#113
AKogut merged 2 commits into
mainfrom
feat/dashboard-foundation

Conversation

@AKogut

@AKogut AKogut commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Closes #55
Closes #56
Closes #23

Summary

Replaces the placeholder HTML server with a real Next.js dashboard: GitHub sign-in, workspace onboarding, ingest token management, and the runs list. This is the first time the platform is visible end to end — a run exported from CI now shows up in a UI.

Architecture

The read queries lived inside apps/api, but the dashboard needs them too. They now live in a new internal package @flakemetry/queries, consumed by both:

  • the API exposes them over tRPC, authorized by an ingest token (machine access)
  • the dashboard calls them from server components, authorized by session + org membership (human access)

One query implementation, two authorization models, no duplication and no cross-service session plumbing.

Auth and multi-tenancy (#55)

Auth.js v5 with the GitHub provider and database sessions. The schema gains User, Account, Session, VerificationToken and a Membership join table (user × org with a role), plus a migration.

Authorization is deliberately split so it stays testable:

  • lib/session.ts — resolves the signed-in user (depends on next-auth)
  • lib/tenant.ts — pure database-backed access checks (listAccessibleProjects, requireProjectAccess), no framework coupling

Every page resolves the user, then re-checks project membership before reading data. A project outside the user's orgs is never queried, it redirects.

Onboarding and tokens (#56)

  • Create a workspace + first project; the creator becomes owner
  • Ingest tokens: create, show once (only a hash is stored, so it cannot be recovered), revoke

Runs list (#23)

Status counts per run (passed / failed / flaky / skipped), branch filter, cursor pagination, and an empty state that tells you how to start ingesting.

Verification

Built the stack and ran a real scenario rather than trusting compilation:

  • OAuth: the authorize URL carries the real client id, correct callback and PKCE
  • Exported 3 runs over real OTLP → API → queue → worker → 11 executions, 3 identities, 3 scores
  • Scoring behaved correctly: auth/logs in scored 0.85 with SAME_SHA_VARIANCE, PASS_ON_RERUN, HIGH_FLIP_RATE, FAIL_ISOLATION; stable tests scored 0.04 with STABLE
  • The runs page rendered those commits, branches and statuses; the branch filter correctly excluded the other branch; tokens create/display/revoke

Access control, verified by request:

Case Result
Project in another org 307 → /
No session 307 → /sign-in
Membership removed access disappears

Five automated tenant-isolation tests cover these so they cannot regress silently.

Testing

  • 42/42 turbo tasks, 128 tests (5 new)

@AKogut AKogut self-assigned this Jul 21, 2026
@AKogut
AKogut merged commit 7ef13d3 into main Jul 21, 2026
2 checks passed
@AKogut
AKogut deleted the feat/dashboard-foundation branch July 21, 2026 15:11
AKogut added a commit that referenced this pull request Jul 21, 2026
## The problem

`docker compose up` — the promise on the front page of the README — was
broken. Replacing the placeholder web app with Next.js in #113 left the
container wiring pointing at the old app, and I verified that work
locally with `next start` instead of verifying the stack. That was the
wrong check.

## What was broken

- The `web` Docker target ran `node apps/web/dist/index.js`, a file
deleted in #113. Next builds to `.next`, so the container exited
immediately.
- The `web` healthcheck probed `/health`, a route the Next app never
had.
- The `web` service had no auth environment, so even a running container
could not sign anyone in.
- The seed produced 24 runs, 1 org and **0 memberships** — the demo data
belonged to nobody and was unreachable after signing in.
- The worker was passed `HEARTBEAT_INTERVAL_MS`, left over from the
placeholder; the real worker reads `POLL_INTERVAL_MS`.

## Fixes

- `web` now builds with Next's `standalone` output and runs `server.js`;
the Dockerfile copies only the standalone bundle and static assets.
- Added a real `/health` route.
- Compose passes `AUTH_SECRET` (required, with a message telling you how
to generate one), the GitHub OAuth pair, `AUTH_URL` and
`AUTH_TRUST_HOST`.
- **First-account bootstrap**: the first account created on a fresh
instance adopts orgs that have no members, so seeded demo data is
reachable instead of orphaned. Orgs that already have a member are never
touched, so this can never hand an existing team's data to a later
signup.
- README quickstart now generates `AUTH_SECRET` and explains the OAuth
app and the bootstrap behaviour.

## Found only because the stack was actually started this time

- **The API healthcheck was broken too**, and had been all along. `wget
localhost` inside Alpine resolves `::1` first while the servers bind
IPv4, so both services reported `unhealthy` forever. It went unnoticed
because nothing depends on the API's health condition. Both healthchecks
now use `127.0.0.1`.
- **A flaky build.** `web#typecheck` ran concurrently with its own
`build`, but the app's tsconfig includes the `.next/types` that `build`
generates — so typecheck intermittently failed on a missing generated
module. Reproduced in one run out of two. Fixed with a package-level
turbo config making `typecheck` depend on `build`; verified with four
consecutive clean runs after deleting `.next` each time.
- **Turbo cached nothing for the web build** — the root config declared
`outputs: ["dist/**"]` while Next writes `.next`.
- Four more tracked build artifacts left behind by the deleted
placeholder app.

## Verification

Brought the whole stack up on a clean volume: every service reports
**healthy**, and the containerized dashboard renders the seeded runs and
the flaky board (scores 0.86 / 0.75 / 0.58). Four tests cover the
adoption rule, including that it refuses an org someone already owns.

42/42 turbo tasks, 132 tests.
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.

Project onboarding + ingest token management UI Dashboard auth (GitHub OAuth) + sessions apps/web — Runs list page

1 participant