luna 787a695fbe fix(v2): UX polish, spec compliance, type safety, race-condition fixes
- Auto-create WebhookToken for new users (first-user and admin-created),
  so setup wizard step 3 immediately has a valid secret to display
- Setup wizard: load webhook secret on step 3 entry (not only on AWS save),
  so skipping AWS setup still shows correct webhook info
- Admin panel: add Edit modal with username and password change for any user
  (spec: 'Edit username or password of any user')
- Webhook handler: only post 'no config' comment on opened/reopened actions,
  not on synchronize or closed — prevents spam on sync events
- deploy.ts: clear stale abort signal at start of runDeploy so a signal meant
  to cancel the previous job cannot accidentally abort the new one
- routes/auth.ts: fix sameSite cookie case to lowercase 'lax' per TypeScript
- Add node-cron type declaration to silence TS7016 for that import
- SPEC.md: fix ec2:ImportKeyPair → ec2:CreateKeyPair (code uses CreateKeyPair)
- example.env: improve comments, add NODE_ENV, add key generation hints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 08:39:36 +00:00

PR Previews (PP)

Self-hosted service that connects to a Gitea instance via webhook. When a PR is opened or updated, PP automatically provisions an AWS EC2 instance, builds and runs the project, and comments a live preview URL back on the PR.

Quick Start

Prerequisites

  • Docker & Docker Compose
  • Gitea instance with admin access
  • AWS account with EC2 permissions

Setup

  1. Clone and configure:

    cp example.env .env
    # Edit .env — at minimum set SESSION_SECRET, PP_BASE_URL, and ENCRYPTION_KEY
    # Generate ENCRYPTION_KEY: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
    
  2. Start services:

    docker compose up -d
    
  3. Open the web UI at http://localhost:5000 (or your configured PP_BASE_URL)

  4. Create your admin account on first login — you'll be prompted for a username and password.

  5. Complete the setup wizard to configure Gitea and AWS credentials.

Architecture

./backend/             # Node.js backend (rjweb-server + Prisma)
./frontend/            # React + Vite + Tailwind CSS
./prisma/schema.prisma # PostgreSQL schema
./docker-compose.yml   # PP itself (backend + db)
./Dockerfile           # Multi-stage build

Environment Variables

Variable Description
DATABASE_URL PostgreSQL connection string
SESSION_SECRET Cookie signing secret (32+ random chars)
PP_BASE_URL Public URL of this PP instance (no trailing slash)
ENCRYPTION_KEY AES-256 key — 64 hex chars (32 bytes). Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
PORT Backend port (default: 5000)
LOG_LEVEL Logging level: trace/debug/info/warn/error (default: info)

PP Commands (in PR comments)

Command Action
/pp rebuild Re-run build on existing EC2 instance
/pp stop Stop and terminate the preview
/pp start Start or restart a stopped/ignored preview
/pp logs Post last 50 lines of logs as a comment
/pp ignore Ignore all future events for this PR

Required AWS IAM Permissions

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "ec2:RunInstances", "ec2:TerminateInstances", "ec2:DescribeInstances",
      "ec2:CreateSecurityGroup", "ec2:DeleteSecurityGroup",
      "ec2:AuthorizeSecurityGroupIngress", "ec2:DescribeSecurityGroups",
      "ec2:CreateKeyPair", "ec2:DeleteKeyPair", "ec2:CreateTags",
      "sts:GetCallerIdentity"
    ],
    "Resource": "*"
  }]
}

Development

# Database (via Docker)
docker compose up -d pp-db

# Run migrations
cd backend && npx prisma migrate dev --schema=../prisma/schema.prisma

# Backend (port 5000)
cd backend && pnpm install && pnpm dev

# Frontend dev server (port 3000, proxies API to backend)
cd frontend && pnpm install && pnpm dev
S
Description
No description provided
Readme Apache-2.0 129 MiB
Languages
TypeScript 97.8%
HTML 1.1%
Dockerfile 0.7%
CSS 0.3%
JavaScript 0.1%