Skip to content
Open
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ VITE_GITHUB_APP_SLUG=tripwire-dev
# VITE_REACT_SCAN_ENABLED=true
# VITE_REACT_GRAB_ENABLED=true

# Optional in local dev. Leave blank to use file-backed PGlite at .tripwire/pglite.
# Required in production.
DATABASE_URL=

# Unkey rate limiting (optional - allows all requests if not set)
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dist-ssr
# Tooling caches
count.txt
todos.json
/.tripwire

# Tool-managed dirs (not source of truth)
/.agents
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ The required vars:
- `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` — from your GitHub OAuth App
- `GITHUB_APP_ID` / `GITHUB_APP_PRIVATE_KEY` / `GITHUB_WEBHOOK_SECRET` — from your GitHub App
- `VITE_GITHUB_APP_SLUG` — the slug from `github.com/apps/{slug}`
- `DATABASE_URL` — Postgres connection string
- `DATABASE_URL` — Postgres connection string. Optional in local dev; leave
blank to use file-backed PGlite at `.tripwire/pglite`.

Optional:

Expand All @@ -63,6 +64,9 @@ Push tables to db:
pnpm db:push
```

If `DATABASE_URL` is blank outside production, `pnpm db:push` and the app use
the local PGlite database. No Docker or local Postgres service is required.

Start the dev server:

```bash
Expand Down
46 changes: 40 additions & 6 deletions apps/web/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,48 @@
import { mkdirSync } from "node:fs"
import { dirname, resolve } from "node:path"
import { fileURLToPath } from "node:url"
import { config } from "dotenv"
import { defineConfig } from "drizzle-kit"

const appDir = dirname(fileURLToPath(import.meta.url))
const repoRoot = resolve(appDir, "../..")
const DEV_DATABASE_DIR = resolve(repoRoot, ".tripwire/pglite")

// .env lives at the monorepo root so all packages (web, future cli) share it.
config({ path: ["../../.env.local", "../../.env", ".env.local", ".env"] })
config({
path: [
resolve(repoRoot, ".env.local"),
resolve(repoRoot, ".env"),
resolve(appDir, ".env.local"),
resolve(appDir, ".env"),
],
})

const isProduction = process.env.NODE_ENV === "production"
const databaseUrl = process.env.DATABASE_URL

if (isProduction && !databaseUrl) {
throw new Error("DATABASE_URL is required in production.")
}

if (!databaseUrl) {
mkdirSync(dirname(DEV_DATABASE_DIR), { recursive: true })
}

export default defineConfig({
out: "./drizzle",
schema: "../../packages/db/src/schema/index.ts",
out: resolve(appDir, "drizzle"),
schema: resolve(repoRoot, "packages/db/src/schema/index.ts"),
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
...(databaseUrl
? {
dbCredentials: {
url: databaseUrl,
},
}
: {
driver: "pglite",
dbCredentials: {
url: DEV_DATABASE_DIR,
},
}),
})
18 changes: 10 additions & 8 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"lint": "biome check . --formatter-enabled=false --assist-enabled=false",
"lint:fix": "biome check --write . --formatter-enabled=false --assist-enabled=false",
"test": "vitest run",
"dev:seed-login": "NODE_ENV=development tsx src/scripts/seed-dev-login.ts",
"typecheck": "tsc --noEmit",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
Expand All @@ -26,6 +27,7 @@
"@ai-sdk/react": "^3.0.186",
"@base-ui/react": "^1.3.0",
"@better-auth/infra": "^0.2.8",
"@electric-sql/pglite": "^0.4.6",
"@json-render/core": "^0.18.0",
"@json-render/react": "^0.18.0",
"@modelcontextprotocol/sdk": "^1.29.0",
Expand All @@ -43,17 +45,17 @@
"@streamdown/code": "^1.1.1",
"@t3-oss/env-core": "^0.13.10",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "latest",
"@tanstack/react-query": "latest",
"@tanstack/react-query-devtools": "latest",
"@tanstack/react-devtools": "0.10.5",
"@tanstack/react-query": "5.100.14",
"@tanstack/react-query-devtools": "5.100.14",
"@tanstack/react-router": "1.168.24",
"@tanstack/react-table": "^8.21.2",
"@tanstack/react-router-devtools": "1.166.13",
"@tanstack/react-router-ssr-query": "1.166.12",
"@tanstack/react-start": "1.167.49",
"@tanstack/react-store": "latest",
"@tanstack/react-store": "0.11.0",
"@tanstack/router-plugin": "1.167.27",
"@tanstack/store": "latest",
"@tanstack/store": "0.11.0",
"@tripwire/ai": "workspace:*",
"@tripwire/auth": "workspace:*",
"@tripwire/core": "workspace:*",
Expand Down Expand Up @@ -110,10 +112,10 @@
},
"devDependencies": {
"@biomejs/biome": "2.2.4",
"@rsbuild/core": "latest",
"@rsbuild/core": "2.0.7",
"@tailwindcss/typography": "^0.5.16",
"@tanstack/devtools-event-client": "latest",
"@tanstack/devtools-vite": "latest",
"@tanstack/devtools-event-client": "0.4.3",
"@tanstack/devtools-vite": "0.7.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.0",
"@types/node": "^22.10.2",
Expand Down
67 changes: 56 additions & 11 deletions apps/web/src/components/layout/auth/login-page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useNavigate } from "@tanstack/react-router"
import { authClient } from "@tripwire/auth/client"
import { useEffect } from "react"
import { useEffect, useState } from "react"
import { Button } from "@tripwire/ui/button"
import { TripwireLogo } from "@tripwire/ui/icons/tripwire-logo"
import { Spinner } from "@tripwire/ui/spinner"

export function LoginPageSkeleton() {
return (
Expand All @@ -13,24 +13,56 @@ export function LoginPageSkeleton() {
}

export function LoginPage() {
const navigate = useNavigate()
const { data: session, isPending } = authClient.useSession()
const [devLoginPending, setDevLoginPending] = useState(false)
const [devLoginError, setDevLoginError] = useState<string | null>(null)

// Redirect to / if already logged in. The root resolver picks up
// from there and routes the user into their org workspace.
// Redirect signed-in users into the app shell. The legacy /home route
// resolves the active workspace and lands on the org-scoped home page.
useEffect(() => {
if (!isPending && session) {
navigate({ to: "/" })
window.location.assign("/home")
}
}, [session, isPending, navigate])
}, [session, isPending])

async function handleLogin() {
async function handleGithubLogin() {
await authClient.signIn.social({
provider: "github",
callbackURL: "/rules",
})
}

async function handleDevLogin() {
setDevLoginPending(true)
setDevLoginError(null)

try {
const seedResponse = await fetch("/api/dev/login", {
method: "POST",
})

if (!seedResponse.ok) {
const responseBody = (await seedResponse.json().catch(() => null)) as {
error?: string
message?: string
} | null

throw new Error(
responseBody?.error ??
responseBody?.message ??
"Dev login is only available while running locally."
)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

window.location.assign("/home")
} catch (error) {
setDevLoginError(
error instanceof Error ? error.message : "Dev login failed."
)
setDevLoginPending(false)
}
}

if (isPending) {
return <LoginPageSkeleton />
}
Expand All @@ -39,13 +71,26 @@ export function LoginPage() {
<div className="flex h-screen w-full shrink-0 flex-col items-center justify-center gap-10 bg-[#191919] px-0 antialiased [font-synthesis:none]">
<TripwireLogo className="h-10 w-10 text-white" />
<Button
onClick={handleLogin}
onClick={import.meta.env.DEV ? handleDevLogin : handleGithubLogin}
variant="outline"
size="sm"
className="border-[#CDCDCD] bg-white text-black hover:bg-white/90"
disabled={devLoginPending}
aria-label={devLoginPending ? "Logging in as dev" : undefined}
className="min-w-24 border-[#CDCDCD] bg-white text-black hover:bg-white/90"
>
Log in
{devLoginPending ? (
<Spinner className="size-4 text-black" />
) : import.meta.env.DEV ? (
"Login as dev"
) : (
"Log in"
)}
</Button>
{devLoginError && (
<p className="max-w-72 text-center text-sm text-tw-error">
{devLoginError}
</p>
)}
</div>
)
}
2 changes: 1 addition & 1 deletion apps/web/src/lib/dev-router-timing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AnyRouter, RouterEvents } from "@tanstack/router-core"
import type { AnyRouter, RouterEvents } from "@tanstack/react-router"

/** Dev-only: logs client navigations from route resolve through first paint hook. */
export function attachDevRouterTiming(router: AnyRouter): void {
Expand Down
Loading
Loading