diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ddbc708..976f824 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. diff --git a/README.md b/README.md index 0a72503..434c558 100644 --- a/README.md +++ b/README.md @@ -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 | @@ -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 @@ -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 | @@ -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 diff --git a/apps/dashboard/.dev.vars.example b/apps/dashboard/.dev.vars.example index 6fe37be..858759e 100644 --- a/apps/dashboard/.dev.vars.example +++ b/apps/dashboard/.dev.vars.example @@ -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/) GITHUB_APP_SLUG= @@ -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= diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md index 3cbdeca..9d346b9 100644 --- a/apps/dashboard/README.md +++ b/apps/dashboard/README.md @@ -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: diff --git a/apps/dashboard/public/login-preview.png b/apps/dashboard/public/login-preview.png new file mode 100644 index 0000000..ffe5342 Binary files /dev/null and b/apps/dashboard/public/login-preview.png differ diff --git a/apps/dashboard/src/components/details/detail-page.tsx b/apps/dashboard/src/components/details/detail-page.tsx index d1792a9..e8aa074 100644 --- a/apps/dashboard/src/components/details/detail-page.tsx +++ b/apps/dashboard/src/components/details/detail-page.tsx @@ -17,7 +17,7 @@ export function DetailPageLayout({ }) { return (
-
+
{main}
{sidebar}
@@ -85,7 +85,7 @@ export function DetailPageSkeletonLayout({ }) { return (
-
+
{main}