Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,21 @@ DiffKit is a **pnpm monorepo** managed with **Turborepo**:
- **TanStack Router** — File-based routing in `apps/dashboard/src/routes/`
- **TanStack Query** — Server state management and caching
- **Drizzle ORM** — Database schema and migrations in `apps/dashboard/src/db/` and `apps/dashboard/drizzle/`
- **Better Auth** — Authentication with a GitHub App
- **Better Auth** — Authentication with a GitHub OAuth App, plus GitHub App user and installation tokens for installed repos
- **Cloudflare D1** — SQLite database at the edge

### GitHub Integration

DiffKit uses a hybrid GitHub auth model:

- The **GitHub OAuth App** signs users in and powers broad user-context reads, including public or external repositories where the GitHub App is not installed.
- The **GitHub App user token** powers installation discovery, including `GET /user/installations`.
- The **GitHub App installation token** is preferred for repo-scoped reads and writes when the app is installed for that owner.

Local development requires both app configs. The OAuth App callback is `/api/auth/callback/github`. The GitHub App user authorization callback is `/api/github/app/callback`, and the GitHub App setup URL is `/?show-org-setup=true` with **Redirect on update** enabled.

Required local variables are documented in `apps/dashboard/.dev.vars.example`. Do not commit real `.dev.vars` values or private keys. If a private key is exposed, revoke it in GitHub App settings and generate a replacement.

### Adding a New Route

Routes live in `apps/dashboard/src/routes/`. TanStack Router uses file-based routing — create a new file and the route is automatically registered.
Expand Down
46 changes: 38 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A fast, design-first GitHub dashboard for developers who want to stay on top of
| Routing | TanStack Router (file-based) |
| Data | TanStack Query + Octokit |
| Database | Cloudflare D1 (SQLite) via Drizzle ORM |
| Auth | Better Auth with GitHub App |
| Auth | Better Auth with GitHub OAuth App + GitHub App |
| Styling | Tailwind CSS 4 + Radix UI |
| Icons | Lucide React |
| Build | Vite 7 + Turborepo |
Expand All @@ -32,7 +32,8 @@ A fast, design-first GitHub dashboard for developers who want to stay on top of

- [Node.js](https://nodejs.org/) (v20+)
- [pnpm](https://pnpm.io/) (v10+)
- A [GitHub App](https://github.com/settings/apps)
- A [GitHub OAuth App](https://github.com/settings/developers) (for user authentication)
- A [GitHub App](https://github.com/settings/apps) (for webhooks and installation management)

### Setup

Expand All @@ -54,23 +55,52 @@ A fast, design-first GitHub dashboard for developers who want to stay on top of
Create a `.dev.vars` file in `apps/dashboard/`:

```
GITHUB_OAUTH_CLIENT_ID=your_oauth_app_client_id
GITHUB_OAUTH_CLIENT_SECRET=your_oauth_app_client_secret
GITHUB_APP_CLIENT_ID=your_github_app_client_id
GITHUB_APP_CLIENT_SECRET=your_github_app_client_secret
GITHUB_APP_ID=your_numeric_github_app_id
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
GITHUB_APP_SLUG=your_github_app_slug
GITHUB_WEBHOOK_SECRET=your_github_webhook_secret
BETTER_AUTH_SECRET=a_random_32_character_string
BETTER_AUTH_URL=http://localhost:3000
```

> DiffKit also accepts the legacy `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` names during migration, but new setups should use the `GITHUB_APP_*` names above.
> DiffKit also accepts the legacy `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` names as a fallback for the OAuth App credentials during migration.

4. **Create and install the GitHub App**
4. **Create the GitHub OAuth App** (for user authentication)

In [GitHub App settings](https://github.com/settings/apps):
In [OAuth App settings](https://github.com/settings/developers):

- Click **New OAuth App**
- Set the callback URL to `http://localhost:3000/api/auth/callback/github`
- Grant the account permission `Email addresses: Read-only`
- Note the **Client ID** and generate a **Client Secret**

The OAuth App handles user login and broad user-context reads. DiffKit requests `repo`, `read:org`, and `user:email` scopes. OAuth is also the fallback path for public or external repositories where the GitHub App is not installed, such as upstream open source repositories.

5. **Create and install the GitHub App** (for webhooks and installations)

In [GitHub App settings](https://github.com/settings/apps):

- Set the callback URL to `http://localhost:3000/api/github/app/callback`
- Set the setup URL to `http://localhost:3000/?show-org-setup=true`
- Enable **Redirect on update**
- Leave **Request user authorization (OAuth) during installation** unchecked
- Note the **Client ID**, generate a **Client Secret**, note the numeric **App ID**, and generate a private key
- Install the app on the repositories or organizations you want DiffKit to access

The GitHub App user authorization flow stores a `ghu_` user-to-server token for installation discovery. Repo-scoped reads and writes prefer GitHub App installation tokens when the app is installed, and fall back to OAuth for external/public repositories.

Store the downloaded private key as an escaped single-line value in `.dev.vars`. GitHub commonly downloads a PKCS#1 key with `BEGIN RSA PRIVATE KEY`; DiffKit normalizes it to the PKCS#8 format required by the GitHub App JWT library at runtime.

```bash
printf 'GITHUB_APP_PRIVATE_KEY="' > /tmp/github-app-private-key.env
sed 's/$/\\n/' /path/to/github-app-private-key.pem | tr -d '\n' >> /tmp/github-app-private-key.env
printf '"\n' >> /tmp/github-app-private-key.env
cat /tmp/github-app-private-key.env
```

Recommended GitHub App permissions derived from the current roadmap:

| Roadmap area | Roadmap items | GitHub App permission | Level | Notes |
Expand Down Expand Up @@ -116,13 +146,13 @@ A fast, design-first GitHub dashboard for developers who want to stay on top of

For local Vite development, set `DEV_TUNNEL_URL` in `apps/dashboard/.dev.vars` to the full public tunnel URL, for example `https://your-subdomain.ngrok-free.app`. The dev server will use it to allow the tunnel host and configure HMR correctly.

5. **Run database migrations**
6. **Run database migrations**

```bash
pnpm --filter dashboard migrate
```

6. **Start the dev server**
7. **Start the dev server**

```bash
pnpm dev
Expand Down
31 changes: 25 additions & 6 deletions apps/dashboard/.dev.vars.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
# GitHub App credentials
# GitHub OAuth App credentials (used for user authentication)
# 1. Go to https://github.com/settings/developers > OAuth Apps > New OAuth App
# 2. Set the callback URL to http://localhost:3000/api/auth/callback/github
# 3. Note the Client ID and generate a Client Secret
# OAuth App tokens support scopes (repo, read:org, user:email) and don't expire.
# Used for login plus public/external repository reads where the GitHub App is not installed.
GITHUB_OAUTH_CLIENT_ID=
GITHUB_OAUTH_CLIENT_SECRET=

# GitHub App credentials (used for installation discovery and app-scoped repo access)
# 1. Go to https://github.com/settings/apps
# 2. Create a new GitHub App
# 3. Set the callback URL to http://localhost:3000/api/auth/callback/github
# 4. Under Permissions & events, grant Email addresses account permission: Read-only
# 5. Install the app on the repositories or organizations you want DiffKit to access
# 2. Create a new GitHub App (or use an existing one)
# 3. Set the callback URL to http://localhost:3000/api/github/app/callback
# 4. Set the setup URL to http://localhost:3000/?show-org-setup=true
# 5. Enable "Redirect on update"
# 6. Leave "Request user authorization (OAuth) during installation" unchecked
# 7. Under Permissions & events, grant the permissions listed in the README
# 8. Install the app on the repositories or organizations you want DiffKit to access
GITHUB_APP_CLIENT_ID=
GITHUB_APP_CLIENT_SECRET=
# Numeric App ID from the GitHub App settings page
GITHUB_APP_ID=
# Private key generated from the GitHub App settings page.
# Store as a quoted value with escaped newlines:
# GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
# GitHub commonly downloads PKCS#1 ("BEGIN RSA PRIVATE KEY"); DiffKit normalizes it for Octokit at runtime.
GITHUB_APP_PRIVATE_KEY=
# The slug from your GitHub App URL (https://github.com/apps/<slug>)
GITHUB_APP_SLUG=

Expand All @@ -17,7 +36,7 @@ GITHUB_WEBHOOK_SECRET=
# Example: https://your-subdomain.ngrok-free.app
DEV_TUNNEL_URL=

# Legacy OAuth-style names are still supported as a fallback during migration.
# Legacy OAuth-style names are still supported as a fallback for GITHUB_OAUTH_* during migration.
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

Expand Down
22 changes: 22 additions & 0 deletions apps/dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ pnpm install
pnpm dev
```

## GitHub Configuration

The dashboard requires both a GitHub OAuth App and a GitHub App.

OAuth App:

- Callback URL: `http://localhost:3000/api/auth/callback/github`
- Environment variables: `GITHUB_OAUTH_CLIENT_ID`, `GITHUB_OAUTH_CLIENT_SECRET`
- Used for login and public/external repository reads.

GitHub App:

- Callback URL: `http://localhost:3000/api/github/app/callback`
- Setup URL: `http://localhost:3000/?show-org-setup=true`
- Enable **Redirect on update**
- Leave **Request user authorization (OAuth) during installation** unchecked
- Environment variables: `GITHUB_APP_CLIENT_ID`, `GITHUB_APP_CLIENT_SECRET`, `GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_APP_SLUG`, `GITHUB_WEBHOOK_SECRET`
- Webhook URL: `/api/webhooks/github`
- Used for installation discovery and app-scoped repo access.

Copy `.dev.vars.example` to `.dev.vars` and fill in the real values. GitHub commonly downloads a PKCS#1 private key with `BEGIN RSA PRIVATE KEY`; the dashboard normalizes it for Octokit at runtime. Never commit `.dev.vars` or private keys.

# Building For Production

To build this application for production:
Expand Down
Binary file added apps/dashboard/public/login-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions apps/dashboard/src/components/details/detail-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function DetailPageLayout({
}) {
return (
<div className="h-full overflow-auto">
<div className="mx-auto grid max-w-7xl gap-16 px-6 py-10 xl:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)]">
<div className="mx-auto grid max-w-7xl gap-16 px-3 py-10 md:px-6 xl:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)]">
<div className="flex min-w-0 flex-col gap-8">{main}</div>
{sidebar}
</div>
Expand Down Expand Up @@ -85,7 +85,7 @@ export function DetailPageSkeletonLayout({
}) {
return (
<div className="h-full overflow-auto">
<div className="mx-auto grid max-w-7xl gap-16 px-6 py-10 xl:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)]">
<div className="mx-auto grid max-w-7xl gap-16 px-3 py-10 md:px-6 xl:grid-cols-[minmax(0,1fr)_minmax(16rem,20rem)]">
<div className="flex min-w-0 flex-col gap-8">{main}</div>
<aside className="flex h-fit flex-col gap-6 xl:sticky xl:top-10">
{Array.from(
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/details/detail-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function DetailParticipantAvatars({
}>;
}) {
return (
<div className="group/participants flex items-center">
<div className="group/participants flex flex-wrap items-center">
{actors.map((actor, index) => (
<Tooltip key={actor.login}>
<TooltipTrigger asChild>
Expand Down
29 changes: 29 additions & 0 deletions apps/dashboard/src/components/details/label-pill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { cn } from "@diffkit/ui/lib/utils";

const sizes = {
sm: "px-1.5 py-px text-[10px]",
md: "px-2.5 py-0.5 text-xs",
};

export function LabelPill({
name,
color,
size = "md",
}: {
name: string;
color: string;
size?: "sm" | "md";
}) {
const hex = color.startsWith("#") ? color : `#${color}`;
return (
<span
className={cn(
"label-pill inline-flex items-center rounded-full font-medium",
sizes[size],
)}
style={{ "--label-color": hex } as React.CSSProperties}
>
{name}
</span>
);
}
Loading
Loading