diff --git a/.astro/settings.json b/.astro/settings.json index 89d7784c..b3dd3daf 100644 --- a/.astro/settings.json +++ b/.astro/settings.json @@ -1,5 +1,5 @@ { "_variables": { - "lastUpdateCheck": 1783539359887 + "lastUpdateCheck": 1783961044818 } } \ No newline at end of file diff --git a/.env.example b/.env.example index a19ee41a..b721b64d 100644 --- a/.env.example +++ b/.env.example @@ -76,6 +76,11 @@ SSH_PORT=2222 STORAGE_TYPE=local STORAGE_PATH=./data/storage +# NAS / dedicated-disk deployments (docker-compose.nas.yml): +# Point every data directory at your NAS mount or dedicated disk. +# OCH_DATA_DIR=/mnt/nas/opencodehub +# STORAGE_PATH=./data/storage # or /mnt/nas/opencodehub/storage with local driver + # S3 Configuration (required when STORAGE_TYPE=s3) # Works with AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, # Ceph RGW, Wasabi, Backblaze B2, etc. @@ -159,6 +164,11 @@ BACKUP_DIR=./backups AIR_GAPPED_MODE=false # ── Observability (optional) ──────────────────────────────── +# Bearer token protecting GET /api/metrics (Prometheus). Set a value in +# production; when empty, the endpoint is public and should be restricted +# at the reverse proxy. +METRICS_TOKEN= + # Grafana Cloud OTLP GRAFANA_OTLP_ENDPOINT= GRAFANA_INSTANCE_ID= diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml index 8dd446cc..42f44dd7 100644 --- a/.github/workflows/ai-review.yml +++ b/.github/workflows/ai-review.yml @@ -22,11 +22,14 @@ jobs: with: bun-version: latest + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Run AI Review env: # Use user's PAT to count towards their GitHub contribution graph, fallback to standard bot token GITHUB_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - GEMINI_API_KEY: ${{ secrets.GEMINIKEY }} + AGENT_API_KEY: ${{ secrets.AGENT_API_KEY }} PR_NUMBER: ${{ github.event.pull_request.number }} REPO: ${{ github.repository }} BASE_SHA: ${{ github.event.pull_request.base.sha }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8d05af2..d3a227ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -255,8 +255,18 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Docker image - run: docker build -t opencodehub:ci -f Dockerfile . + uses: docker/build-push-action@v5 + with: + context: . + load: true + tags: opencodehub:ci + file: Dockerfile + cache-from: type=gha + cache-to: type=gha,mode=max - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master @@ -347,9 +357,11 @@ jobs: DATABASE_DRIVER: postgres - name: Start server and run perf check run: | - bun run dev & + HOST=127.0.0.1 PORT=4321 node ./dist/server/entry.mjs & SERVER_PID=$! - sleep 8 + sleep 5 + curl -s http://127.0.0.1:4321/api/health > /dev/null || true + curl -s http://127.0.0.1:4321/api/metrics > /dev/null || true bun run perf:baseline kill $SERVER_PID 2>/dev/null || true env: @@ -360,6 +372,7 @@ jobs: INTERNAL_HOOK_SECRET: "ci-perf-hook" CSRF_SKIP_DEV: "true" RATE_LIMIT_SKIP_DEV: "true" + RATE_LIMIT_ENABLED: "false" SKIP_REDIS_CHECK: "1" PERF_BASE_URL: http://127.0.0.1:4321 PERF_PATHS: /api/health,/api/metrics @@ -460,6 +473,9 @@ jobs: runs-on: ubuntu-latest needs: [build, e2e] if: github.event_name == 'push' && github.ref == 'refs/heads/main' + permissions: + contents: read + id-token: write steps: - uses: actions/checkout@v4 @@ -482,7 +498,6 @@ jobs: done echo "::error::bun install failed after 3 attempts" exit 1 - working-directory: cli - name: Build CLI run: bun run build @@ -500,7 +515,7 @@ jobs: quality-gate: name: Quality Gate runs-on: ubuntu-latest - needs: [lint, docs-parity, security, test, e2e, container-scan, sast, perf, build] + needs: [lint, docs-parity, security, test, e2e, container-scan, sast, performance, build] if: always() steps: - name: Check all gates @@ -515,13 +530,13 @@ jobs: echo "Docs Parity: ${{ needs.docs-parity.result }}" echo "Security Gates: ${{ needs.security.result }}" - echo "Perf Gate: ${{ needs.perf.result }}" + echo "Perf Gate: ${{ needs.performance.result }}" if [[ "${{ needs.lint.result }}" != "success" ]] || \ [[ "${{ needs.docs-parity.result }}" != "success" ]] || \ [[ "${{ needs.security.result }}" != "success" ]] || \ [[ "${{ needs.test.result }}" != "success" ]] || \ - [[ "${{ needs.perf.result }}" != "success" ]] || \ + [[ "${{ needs.performance.result }}" != "success" ]] || \ [[ "${{ needs.build.result }}" != "success" ]]; then echo "::error::Quality gate FAILED — required checks did not pass" exit 1 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 88b091a4..c4d90ca6 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -8,7 +8,8 @@ on: env: REGISTRY: docker.io - IMAGE_NAME: swadhinbiswas/opencodehub + IMAGE_NAME: opencodehub/opencodehub + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: "true" jobs: build-and-push: @@ -21,6 +22,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -30,8 +34,8 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - - name: Extract metadata - id: meta + - name: Extract Metadata (App) + id: meta-app uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} @@ -42,41 +46,83 @@ jobs: type=sha,prefix= type=raw,value=latest,enable={{is_default_branch}} labels: | - org.opencontainers.image.title=OpenCodeHub - org.opencontainers.image.description=A modern, self-hosted Git platform for teams that want speed, control, and clean workflows. + org.opencontainers.image.title=OpenCodeHub Platform + org.opencontainers.image.description=A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review. + org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub + org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub + org.opencontainers.image.licenses=MIT + + - name: Extract Metadata (Worker) + id: meta-worker + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-worker + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + labels: | + org.opencontainers.image.title=OpenCodeHub Worker + org.opencontainers.image.description=Background task worker for OpenCodeHub (queues, webhooks, notifications). org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub org.opencontainers.image.licenses=MIT - - name: Build and push app image + - name: Extract Metadata (Runner) + id: meta-runner + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + labels: | + org.opencontainers.image.title=OpenCodeHub Runner + org.opencontainers.image.description=Docker-in-Docker CI/CD runner for OpenCodeHub pipelines. + org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub + org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub + org.opencontainers.image.licenses=MIT + + - name: Build and Push App Image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta-app.outputs.tags }} + labels: ${{ steps.meta-app.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64 - - name: Build and push runner image + - name: Build and Push Worker Image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.worker + push: true + tags: ${{ steps.meta-worker.outputs.tags }} + labels: ${{ steps.meta-worker.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + - name: Build and Push Runner Image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.runner push: true - tags: | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner:latest - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner:${{ github.sha }} - labels: | - org.opencontainers.image.title=OpenCodeHub Runner - org.opencontainers.image.description=CI/CD runner for OpenCodeHub - org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub - org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub - org.opencontainers.image.licenses=MIT + tags: ${{ steps.meta-runner.outputs.tags }} + labels: ${{ steps.meta-runner.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 - name: Update Docker Hub Repository Description uses: peter-evans/dockerhub-description@v4 @@ -84,5 +130,5 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} repository: ${{ env.IMAGE_NAME }} - readme-filepath: ./README.md - short-description: "A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review." + readme-filepath: ./DOCKERHUB.md + short-description: "Self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI code review" diff --git a/.gitignore b/.gitignore index 54a4064c..8db38cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -61,8 +61,9 @@ pci/ # Docker .docker/ -# Drizzle -drizzle/meta/ +# Drizzle — migrations and the migration journal must be committed so that +# runtime containers can apply schema deterministically (scripts/migrate.ts) +# drizzle/ # Monaco Editor cache .monaco/ diff --git a/.mimocode/.cron-lock b/.mimocode/.cron-lock new file mode 100644 index 00000000..730a58ef --- /dev/null +++ b/.mimocode/.cron-lock @@ -0,0 +1 @@ +{"pid":8618,"startedAt":1784441536749} \ No newline at end of file diff --git a/.mimocode/plans/1784309305424-sunny-pixel.md b/.mimocode/plans/1784309305424-sunny-pixel.md new file mode 100644 index 00000000..42732ee4 --- /dev/null +++ b/.mimocode/plans/1784309305424-sunny-pixel.md @@ -0,0 +1,57 @@ +# GitHub-like Profile Portfolio Page + +## Current State +The profile page at `src/pages/[owner]/index.astro` already has: +- Avatar, display name, username, bio +- Details (company, location, email, website, join date) +- Followers/following +- Contribution graph +- Repository list +- Activity feed + +## What Needs to Change + +On GitHub, when you visit `github.com/username`, you see: +1. **Left sidebar**: Avatar (large), name, username, bio, details, followers/following, "Edit profile" button +2. **Main content**: + - "Read me" section rendered from `username/username` repository's README.md + - "Pinned" repositories section (6 repos max, customizable) + - Contribution graph + - Activity feed + +The current OpenCodeHub page has this mostly but the layout and "Read me" section from the special repo are missing. + +## Implementation Plan + +### 1. Auto-create profile repository on user registration +- When a user registers, automatically create a `{username}/{username}` repository (like GitHub) +- Seed it with a default README.md containing the user's name and bio +- This repo should be private by default but its README is shown on the public profile + +### 2. Update profile page layout +- Move to GitHub's 2-column layout: left sidebar (avatar + details) + main content +- Left sidebar: Large avatar, name, username, bio, details, followers/following +- Main content: Render README.md from profile repo, pinned repos, contribution graph, activity + +### 3. Add "Read me" section +- Fetch `README.md` from `{username}/{username}` repository +- Render it as markdown in the profile page (like GitHub does) +- If no README exists, show the current default layout + +### 4. Add pinned repositories section +- Allow users to pin up to 6 repositories to their profile +- Store pinned repo IDs in user settings or a separate table +- Show pinned repos in a grid below the README + +### Files to Modify +- `src/pages/[owner]/index.astro` — Main profile page +- `src/pages/api/repos/index.ts` — Auto-create profile repo on registration +- `src/pages/register.astro` — Trigger profile repo creation +- `src/components/repo/ProfileREADME.tsx` — New component to render profile README +- `src/components/repo/PinnedRepos.tsx` — New component for pinned repos grid + +### Verification +1. Register a new user → profile repo auto-created with README +2. Visit `/{username}` → shows GitHub-like profile with README, pinned repos, contribution graph +3. Edit profile repo README → profile page updates +4. Pin/unpin repositories → grid updates diff --git a/AGENTS.md b/AGENTS.md index 3eefd920..74362f4c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -294,7 +294,7 @@ Configured via `STORAGE_TYPE` env var (`local` or `s3`). npm install -g opencodehub-cli # Auth -och auth login --url http://localhost:3000 +och auth login --url http://localhost:4321 # Stack workflow och stack create feature/part-1 # Create stacked branch @@ -343,7 +343,7 @@ npm run db:push bun run scripts/seed-admin.ts # Create admin user # Dev -npm run dev # Start dev server (port 3000) +npm run dev # Start dev server (port 4321) npm run git:start # Start SSH git server (port 2222) npm run worker:start # Start background worker npm run runner:start # Start CI runner @@ -392,7 +392,9 @@ docker-compose up -d # Full stack with postgres + redis + runner 4. Configure Redis for distributed locking 5. Enable rate limiting (`RATE_LIMIT_ENABLED=true`) 6. Set up email (SMTP) for notifications -7. Configure storage backend (S3/GCS recommended for multi-node) +7. Configure storage backend (`local` for single-host/NAS, S3-compatible for multi-node) +8. Set `METRICS_TOKEN` to protect `GET /api/metrics` +9. Apply committed migrations (`bun run migrate` — runs automatically in containers) ### Vercel/Edge - Turso (LibSQL) recommended for edge deployment diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index d03f5188..5115a224 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -87,31 +87,28 @@ DATABASE_DRIVER=postgres DATABASE_URL=postgresql://user:password@host:5432/opencodehub ``` -**Storage (Use cloud storage, not local):** +**Storage (local or S3-compatible — only two backends supported):** ```bash +# local (default): filesystem, ideal for a dedicated disk or NAS mount +STORAGE_TYPE=local +STORAGE_PATH=/mnt/nas/opencodehub/storage # point at your NAS mount / dedicated disk + +# or S3-compatible (AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, Ceph RGW, ...) STORAGE_TYPE=s3 STORAGE_BUCKET=opencodehub-production STORAGE_REGION=us-east-1 -S3_ACCESS_KEY= -S3_ACCESS_KEY= -S3_SECRET_KEY= +STORAGE_ENDPOINT= # leave empty for AWS S3; set for other vendors +STORAGE_ACCESS_KEY_ID= +STORAGE_SECRET_ACCESS_KEY= ``` -**Google Drive + Turso Stack:** -See [docs/GDRIVE_STACK.md](docs/GDRIVE_STACK.md) for detailed setup. -```bash -STORAGE_TYPE=gdrive -GOOGLE_CLIENT_ID=... -GOOGLE_CLIENT_SECRET=... -GOOGLE_REFRESH_TOKEN=... -GOOGLE_FOLDER_ID=... -``` +> Only `local` and `s3` storage backends are supported. Google Drive, +> OneDrive, Dropbox, FTP, GCS, Azure, and rclone-as-adapter have been +> removed; use the `s3` driver with a vendor-specific `STORAGE_ENDPOINT`. +> See `docs/guides/storage-adapters.md`. **Optional but Recommended:** ```bash -# Error Monitoring -SENTRY_DSN=https://your-sentry-dsn - # Email SMTP_HOST=smtp.example.com SMTP_PORT=587 @@ -120,7 +117,7 @@ SMTP_PASSWORD= SMTP_FROM=noreply@yourcompany.com # Redis (for session management) -REDIS_URL=redis://localhost:6379 +REDIS_URL=redis://:your-password@localhost:6379 ``` ### Deployment Steps @@ -169,7 +166,7 @@ services: build: . restart: always ports: - - "3000:3000" + - "4321:4321" env_file: .env.production depends_on: - postgres @@ -179,7 +176,7 @@ services: environment: NODE_ENV: production healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] + test: ["CMD", "curl", "-f", "http://localhost:4321/api/health"] interval: 30s timeout: 10s retries: 3 @@ -208,14 +205,15 @@ volumes: ``` ```bash -# Deploy with Docker -docker-compose -f docker-compose.prod.yml up -d +# Deploy with the repository's compose files (kept in sync with the codebase) +docker compose up -d # quickstart +docker compose -f docker-compose.production.yml --profile production up -d # Caddy TLS # View logs -docker-compose -f docker-compose.prod.yml logs -f app +docker compose logs -f app # Create admin user in container -docker-compose exec app bun run scripts/seed-admin.ts +docker compose exec app bun run scripts/seed-admin.ts ``` ### Reverse Proxy (Nginx) @@ -237,7 +235,7 @@ server { # Proxy configuration location / { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -251,7 +249,7 @@ server { # Git operations (larger timeouts) location ~ ^/[^/]+/[^/]+\.git/ { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_read_timeout 600s; proxy_send_timeout 600s; proxy_buffer_size 128k; @@ -481,7 +479,7 @@ curl -X POST https://git.yourcompany.com/api/admin/test-email \ - **API Documentation:** https://git.yourcompany.com/api/docs - **Admin Panel:** https://git.yourcompany.com/admin - **User Guide:** See README.md -- **Security Best Practices:** See production_analysis.md +- **Security Best Practices:** See docs/administration/security.md --- diff --git a/DOCKERHUB.md b/DOCKERHUB.md new file mode 100644 index 00000000..3a002735 --- /dev/null +++ b/DOCKERHUB.md @@ -0,0 +1,209 @@ +# OpenCodeHub + +[![Docker Image Size](https://img.shields.io/docker/image-size/opencodehub/opencodehub/latest?style=flat-square&color=blue)](https://hub.docker.com/r/opencodehub/opencodehub) +[![Docker Pulls](https://img.shields.io/docker/pulls/opencodehub/opencodehub?style=flat-square&color=emerald)](https://hub.docker.com/r/opencodehub/opencodehub) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT) +[![Documentation](https://img.shields.io/badge/Docs-docs.opencodehub.space-indigo?style=flat-square)](https://docs.opencodehub.space) + +**The self-hosted Git platform built for speed, control, and modern software engineering workflows.** + +OpenCodeHub is an open-source alternative to GitHub/GitLab featuring native Graphite-style **Stacked PRs**, a **Merge Queue with speculative builds**, a **GitHub Actions-compatible CI/CD engine**, and multi-provider **AI Code Review**. + +--- + +## Key Features + +- 🥞 **Stacked PRs** — Native stacked branch workflows in Web UI & CLI (`och stack create/submit/sync`) +- 🚦 **Merge Queue** — Priority lanes, speculatively tested builds, and auto-merge controls +- ⚡ **CI/CD Pipeline Engine** — GitHub Actions workflow runner using Docker-in-Docker execution +- 🤖 **AI Code Review** — Multi-provider support (OpenAI, Anthropic, Gemini, Groq, local Ollama) +- 🔒 **Enterprise Security** — Secret scanning, Trivy vulnerability scans, license compliance, OIDC SSO, 2FA/TOTP, and path permissions +- 📦 **Pluggable Storage** — Local filesystem, AWS S3, Cloudflare R2, MinIO, Ceph, Garage, and any S3-compatible backend +- 🗄️ **Multi-Database Support** — PostgreSQL (recommended for production), SQLite, and Turso/LibSQL +- 🧰 **Developer CLI** — `opencodehub-cli` (`och`) with interactive terminal cockpit (`och focus`) and stack visualization + +--- + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CLIENTS │ +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ OPENCODEHUB PLATFORM │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Web UI │ │ REST API │ │ GraphQL Endpoint │ │ +│ │ (Astro+React)││ (140+ routes)││ (src/pages/api/) │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Git Server │ │ SSH Server │ │ Pipeline Runner │ │ +│ │ (HTTP RPC) │ │ (ssh2) │ │ (Docker executor) │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ PERSISTENCE & INFRASTRUCTURE │ +│ PostgreSQL / SQLite │ Redis │ S3 / MinIO / Local │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Quick Start + +### 1. Single Container (Quick Test) + +```bash +docker run -d \ + --name opencodehub \ + -p 4321:4321 \ + -p 2222:2222 \ + -v opencodehub-data:/data \ + -e JWT_SECRET=$(openssl rand -hex 32) \ + -e SESSION_SECRET=$(openssl rand -hex 32) \ + -e INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) \ + -e SITE_URL=http://localhost:4321 \ + opencodehub/opencodehub:latest +``` + +Open **http://localhost:4321** in your browser to get started. + +--- + +### 2. Production Docker Compose Stack (Recommended) + +```yaml +version: "3.8" + +services: + app: + image: opencodehub/opencodehub:latest + container_name: opencodehub-app + restart: always + ports: + - "4321:4321" # Web UI & API + - "2222:2222" # Git SSH Server + environment: + - NODE_ENV=production + - SITE_URL=https://git.yourdomain.com + - DATABASE_DRIVER=postgres + - DATABASE_URL=postgresql://opencodehub:secretpass@postgres:5432/opencodehub?sslmode=disable + - REDIS_URL=redis://redis:6379 + - JWT_SECRET=${JWT_SECRET} + - SESSION_SECRET=${SESSION_SECRET} + - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET} + - RUNNER_SECRET=${RUNNER_SECRET} + - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY} + volumes: + - app-data:/data + depends_on: + - postgres + - redis + + worker: + image: opencodehub/opencodehub-worker:latest + container_name: opencodehub-worker + restart: always + environment: + - NODE_ENV=production + - DATABASE_DRIVER=postgres + - DATABASE_URL=postgresql://opencodehub:secretpass@postgres:5432/opencodehub?sslmode=disable + - REDIS_URL=redis://redis:6379 + volumes: + - app-data:/data + depends_on: + - postgres + - redis + + runner: + image: opencodehub/opencodehub-runner:latest + container_name: opencodehub-runner + restart: always + privileged: true + environment: + - OPENCODEHUB_URL=http://app:4321 + - RUNNER_SECRET=${RUNNER_SECRET} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - app + + postgres: + image: postgres:16-alpine + container_name: opencodehub-postgres + restart: always + environment: + POSTGRES_USER: opencodehub + POSTGRES_PASSWORD: secretpass + POSTGRES_DB: opencodehub + volumes: + - pg-data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + container_name: opencodehub-redis + restart: always + volumes: + - redis-data:/data + +volumes: + app-data: + pg-data: + redis-data: +``` + +Start the stack: +```bash +docker compose up -d +docker compose exec app bun run scripts/seed-admin.ts +``` + +--- + +## Environment Variables Reference + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `SITE_URL` | Yes | `http://localhost:4321` | Base URL of your platform (e.g. `https://git.company.com`) | +| `DATABASE_DRIVER` | No | `postgres` | Database driver (`postgres`, `sqlite`, `turso`) | +| `DATABASE_URL` | Yes | — | Connection string (PostgreSQL/SQLite) | +| `REDIS_URL` | Yes | — | Redis connection URI for sessions & locks | +| `JWT_SECRET` | Yes | — | 32+ char secret for JWT token signing | +| `SESSION_SECRET` | Yes | — | 32+ char secret for cookie encryption | +| `INTERNAL_HOOK_SECRET` | Yes | — | Shared secret for Git hook callbacks | +| `STORAGE_TYPE` | No | `local` | Storage backend (`local` or `s3`) | +| `STORAGE_BUCKET` | If `STORAGE_TYPE=s3` | — | S3 bucket name | +| `STORAGE_REGION` | If `STORAGE_TYPE=s3` | `us-east-1` | S3 region | +| `STORAGE_ENDPOINT` | Optional | — | S3-compatible custom endpoint (MinIO / R2 / Garage) | +| `STORAGE_ACCESS_KEY_ID` | If `STORAGE_TYPE=s3` | — | Access Key ID | +| `STORAGE_SECRET_ACCESS_KEY` | If `STORAGE_TYPE=s3` | — | Secret Access Key | +| `METRICS_TOKEN` | Optional | — | Bearer token for `GET /api/metrics` | + +--- + +## OpenCodeHub CLI (`och`) + +Install the official CLI to manage stacks, merge queues, and reviews: + +```bash +npm install -g opencodehub-cli + +# Authenticate with your self-hosted instance +och auth login --url https://git.yourdomain.com + +# Create stacked PR branches +och stack create feature/part-1 +och stack submit +``` + +--- + +## Useful Links & Community + +- **Official Repository**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub) +- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space) +- **Bug Tracker**: [github.com/swadhinbiswas/OpencodeHub/issues](https://github.com/swadhinbiswas/OpencodeHub/issues) +- **License**: MIT diff --git a/Dockerfile b/Dockerfile index dae0d485..e89d2e67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ LABEL org.opencontainers.image.title="OpenCodeHub" \ org.opencontainers.image.source="https://github.com/swadhinbiswas/OpencodeHub" \ org.opencontainers.image.licenses="MIT" -# Install dependencies +# Install ALL dependencies (needed for build including CSS tooling) FROM base AS deps RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* COPY package.json bun.lock ./ @@ -16,6 +16,19 @@ RUN for i in 1 2 3; do \ (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \ done +# Install production dependencies only (for runtime image) +FROM base AS prod-deps +RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* +COPY package.json bun.lock ./ +COPY cli/package.json ./cli/package.json +# Clear Bun install cache to avoid stale/corrupt node-gyp entries across layers +RUN rm -rf /root/.bun/install/cache || true +RUN for i in 1 2 3; do \ + bun install --frozen-lockfile --production && break || \ + (echo "prod bun install attempt $i failed, clearing cache and retrying in 10s..." && \ + rm -rf /root/.bun/install/cache && sleep 10); \ + done + # Build the application FROM base AS builder COPY --from=deps /app/node_modules ./node_modules @@ -24,34 +37,39 @@ ENV SKIP_REDIS_CHECK=1 RUN bun run build # Production image -FROM oven/bun:1 AS runner +FROM oven/bun:1-slim AS runner WORKDIR /app -# Install git, ssh, and bash (needed for git operations and entrypoint) -RUN apt-get update && apt-get install -y git openssh-client bash && rm -rf /var/lib/apt/lists/* +# Install git, ssh, bash, and wget (git operations, entrypoint, healthcheck) +RUN apt-get update && apt-get install -y --no-install-recommends git openssh-client bash wget && \ + rm -rf /var/lib/apt/lists/* # Create data directories -RUN mkdir -p /data/repos /data/storage /data/cache /data/ssh && \ +RUN mkdir -p /data/repositories /data/storage /data/cache /data/ssh && \ chown -R bun:bun /data -# Copy built application +# Copy only what's needed for runtime COPY --from=builder --chown=bun:bun /app/dist ./dist -COPY --from=builder --chown=bun:bun /app/node_modules ./node_modules +COPY --from=prod-deps --chown=bun:bun /app/node_modules ./node_modules COPY --from=builder --chown=bun:bun /app/package.json ./ -# Copy drizzle config and schema for migrations +# Copy drizzle config, committed migrations, and full source tree. +# The full src/ is required so raw-TS entrypoints (scripts/ssh-server.ts, +# scripts/worker.ts) can resolve the "@/..." path aliases via tsconfig. COPY --from=builder --chown=bun:bun /app/drizzle.config.ts ./ -COPY --from=builder --chown=bun:bun /app/src/db ./src/db +COPY --from=builder --chown=bun:bun /app/drizzle ./drizzle +COPY --from=builder --chown=bun:bun /app/src ./src COPY --from=builder --chown=bun:bun /app/tsconfig.json ./ -# Copy entrypoint script +# Copy runtime scripts (migrate, ssh-server, worker) and entrypoint +COPY --from=builder --chown=bun:bun /app/scripts ./scripts COPY --chown=bun:bun docker-entrypoint.sh ./ # Set environment variables ENV HOST=0.0.0.0 ENV PORT=4321 ENV DATA_DIR=/data -ENV REPOS_PATH=/data/repos +ENV GIT_REPOS_PATH=/data/repositories ENV STORAGE_PATH=/data/storage ENV CACHE_PATH=/data/cache ENV SSH_PATH=/data/ssh diff --git a/Dockerfile.worker b/Dockerfile.worker new file mode 100644 index 00000000..5a7ba5a3 --- /dev/null +++ b/Dockerfile.worker @@ -0,0 +1,44 @@ +FROM oven/bun:1 AS base +WORKDIR /app + +# Install dependencies +FROM base AS deps +RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* +COPY package.json bun.lock ./ +COPY cli/package.json ./cli/package.json +RUN for i in 1 2 3; do \ + bun install --frozen-lockfile && break || \ + (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \ + done + +# Production image +FROM oven/bun:1 AS runner +WORKDIR /app + +# Install git (needed for git operations) +RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* + +# Create data directories +RUN mkdir -p /data/repositories /data/storage /data/cache && \ + chown -R bun:bun /data + +# Copy node_modules and source +COPY --from=deps --chown=bun:bun /app/node_modules ./node_modules +COPY --from=deps --chown=bun:bun /app/package.json ./ +COPY --from=deps --chown=bun:bun /app/cli ./cli +COPY --from=deps --chown=bun:bun /app/tsconfig.json ./ + +# Copy source files needed for worker +COPY --from=deps --chown=bun:bun /app/src/lib ./src/lib +COPY --from=deps --chown=bun:bun /app/src/db ./src/db +COPY --from=deps --chown=bun:bun /app/scripts/worker.ts ./scripts/worker.ts + +# Set environment variables +ENV NODE_ENV=production +ENV DATA_DIR=/data + +# Switch to non-root user +USER bun + +# Start the worker +CMD ["bun", "scripts/worker.ts"] diff --git a/PRODUCTION_READINESS.md b/PRODUCTION_READINESS.md index c83a3b8b..0d40b95b 100644 --- a/PRODUCTION_READINESS.md +++ b/PRODUCTION_READINESS.md @@ -1,6 +1,6 @@ # OpenCodeHub Production Readiness Report -**Date:** 2026-04-30 +**Date:** 2026-08-02 **Auditor:** Agent Code Review **Status:** Production-ready @@ -11,11 +11,25 @@ OpenCodeHub is a self-hosted Git platform with enterprise-grade features including Git hosting (HTTP/SSH), PRs, issues, stacked PRs, merge queue with speculative builds, CI/CD pipeline engine, and a comprehensive CLI. **Current State:** -- Lint: 0 errors, 0 warnings, 491 hints +- Lint: 0 errors, 0 warnings - Typecheck: 0 errors -- Tests: 546 passing, 0 failing (114 test files, 100% pass rate) -- Security: Rate limiting, CSRF, JWT, env validation, secret encryption all implemented -- Docker: Full docker-compose setup with PostgreSQL, Redis, and CI runner +- Tests: 546 passing, 0 failing (100% pass rate) +- Security: Rate limiting, CSRF, JWT, env validation, secret encryption, authenticated metrics all implemented +- Docker: Production-safe compose stacks (no default secrets, migrations applied at boot, worker + SSH services included) +- Storage: `local` (filesystem/NAS) and `s3` (any S3-compatible store) only +- Migrations: Versioned `drizzle/` folder committed and applied deterministically at container boot + +## Recent Production Hardening + +| Area | Change | +|------|--------| +| Secrets | `.env.backup-*` removed from git tracking; docker-compose no longer ships default secrets | +| Migrations | Committed `drizzle/0000_*.sql`; applied at boot via `scripts/migrate.ts` (no runtime `generate`) | +| `npm start` | Now runs the built server (`node ./dist/server/entry.mjs`) instead of `astro dev` | +| Docker | `ssh-git` and `worker` services now run the correct process (`PROCESS_TYPE`); image copies `scripts/` + `drizzle/` | +| Metrics | `GET /api/metrics` protected by optional `METRICS_TOKEN` bearer auth | +| Env vars | Standardized on `DATABASE_DRIVER`, `GIT_REPOS_PATH`, `GIT_SSH_HOST_KEY`, `STORAGE_PATH` across all compose files | +| Storage docs | Removed all references to removed backends (GDrive/Azure/GCS/etc.); only `local` + `s3` documented | --- @@ -90,14 +104,18 @@ DATABASE_DRIVER=postgres DATABASE_URL=postgresql://user:pass@localhost:5432/opencodehub # Redis (required for distributed rate limiting, sessions, and queues) -REDIS_URL=redis://localhost:6379 - -# Storage (use S3/GCS/Azure in production, not local) -STORAGE_DRIVER=s3 -S3_BUCKET=your-bucket -S3_REGION=us-east-1 -S3_ACCESS_KEY= -S3_SECRET_KEY= +REDIS_URL=redis://:password@localhost:6379 + +# Storage — only local and s3 are supported (local is ideal for a NAS/dedicated disk) +STORAGE_TYPE=local +STORAGE_PATH=/mnt/nas/opencodehub/storage + +# S3-compatible (AWS S3, MinIO, R2, Garage, ...) when STORAGE_TYPE=s3: +STORAGE_BUCKET=your-bucket +STORAGE_REGION=us-east-1 +STORAGE_ENDPOINT= +STORAGE_ACCESS_KEY_ID= +STORAGE_SECRET_ACCESS_KEY= ``` ### Docker Compose (Recommended) @@ -128,7 +146,7 @@ Exposes: 1. **Modular Monolith** — Easy to deploy as single container or scale individual workers 2. **Database Flexibility** — PostgreSQL for production, SQLite/Turso for dev/edge -3. **Storage Abstraction** — 8+ backends via adapter pattern (S3, GCS, Azure, Google Drive, OneDrive, Dropbox, FTP, rclone) +3. **Storage Abstraction** — two backends via adapter pattern: `local` (filesystem; ideal for a dedicated disk or NAS mount) and `s3` (any S3-compatible store) 4. **Git Protocol Integration** — Native HTTP + SSH via `git` CLI and `ssh2` 5. **CI/CD Engine** — GitHub Actions-compatible with Docker-based execution 6. **Stacked PRs** — Graphite-style workflows in web + CLI diff --git a/README.md b/README.md index 7cd8b1dd..578ad0d0 100644 --- a/README.md +++ b/README.md @@ -1,242 +1,137 @@ -# OpenCodeHub -

- OpenCodeHub Logo + OpenCodeHub

-

- A modern, self-hosted Git platform for teams that want speed, control, and clean workflows. -

+

The self-hosted Git platform that doesn't compromise.

- CLI version - License - Docs - CI + CI + License + CLI version + Docs + Docker

--- -## Table of Contents +OpenCodeHub is a self-hosted Git platform with **stacked PRs**, **merge queue**, **CI/CD pipelines**, and **AI code review**. One platform for everything your team needs — no vendor lock-in, no per-seat pricing. -- [Why OpenCodeHub](#why-opencodehub) -- [Features](#features) -- [Quick Start](#quick-start) -- [Architecture](#architecture) -- [Documentation](#documentation) -- [CLI](#cli) -- [API](#api) -- [Security](#security) -- [Deployment](#deployment) -- [Contributing](#contributing) -- [License](#license) +**[Documentation](https://docs.opencodehub.space)** · **[Deploy in 5 minutes](#deploy)** · **[CLI Reference](https://docs.opencodehub.space/reference/cli-commands/)** --- -## Why OpenCodeHub +## Demo + + +[![OpenCodeHub Demo](https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg)](https://youtu.be/VIDEO_ID) -Most teams eventually hit the same pain points: +*Watch the full walkthrough — deployment, stacked PRs, AI review, and merge queue in action.* -- **PRs are too large to review quickly** — Stacked PRs let you break changes into reviewable pieces -- **Tooling is split across many services** — One platform for Git, PRs, CI/CD, issues, and wiki -- **Data residency and security requirements** — Self-hosted by default, you control everything -- **Merge queue complexity** — Stack-aware merge queue with speculative builds -- **Slow review cycles** — AI-powered code review catches issues before human review +--- + +## Why OpenCodeHub? -OpenCodeHub is built for that reality. +| Problem | Solution | +|---------|----------| +| PRs too large to review | **Stacked PRs** — break changes into small, dependent branches | +| Merge conflicts on main | **Merge Queue** — stack-aware ordering with speculative CI builds | +| Slow review cycles | **AI Code Review** — catches bugs before humans even look | +| Split across 5+ services | **All-in-one** — Git, PRs, CI/CD, issues, wiki in one place | +| Data leaves your servers | **Self-hosted** — your code stays on your hardware | --- ## Features ### Core Platform - -| Feature | Status | Description | -|---------|--------|-------------| -| **Git Hosting** | Ready | HTTP (Smart Protocol) + SSH git push/pull | -| **Pull Requests** | Ready | Comments, reviews, drafts, approvals, suggested changes | -| **Issues** | Ready | Labels, milestones, project boards | -| **Wiki** | Ready | Repository wiki with revision history | -| **Organizations** | Ready | Teams, collaborators, repository settings | -| **Branch Protection** | Ready | Required reviews, status checks, push restrictions | +- **Git Hosting** — HTTP smart protocol + SSH push/pull, forks, mirroring, LFS +- **Pull Requests** — Inline comments, approvals, suggested changes, draft PRs +- **Issues & Projects** — Labels, milestones, custom fields, kanban boards +- **Wiki** — Repository wiki with revision history +- **Organizations** — Teams, collaborators, role-based access control ### Delivery Workflows - -| Feature | Status | Description | -|---------|--------|-------------| -| **Stacked PRs** | Ready | Graphite-style stack workflows in web + CLI | -| **Merge Queue** | Ready | Stack-aware queue with speculative builds and priority lanes | -| **CI/CD Pipelines** | Ready | GitHub Actions-compatible engine + Docker-based runner | -| **Webhooks** | Ready | Outbound webhooks with event filtering | -| **Automations** | Ready | Workflow automation rules | - -### Security & Governance - -| Feature | Status | Description | -|---------|--------|-------------| -| **Authentication** | Ready | JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP | -| **Authorization** | Ready | RBAC with roles, team permissions, collaborator levels | -| **Rate Limiting** | Ready | Redis-backed per-endpoint rate limiting | -| **CSRF Protection** | Ready | Double-submit cookie pattern | -| **Secret Scanning** | Ready | Detect secrets in commits | -| **Audit Logging** | Ready | Track all administrative actions | +- **Stacked PRs** — Graphite-style stacked branches with web + CLI support +- **Merge Queue** — Stack-aware queue with speculative builds and priority lanes +- **CI/CD Pipelines** — GitHub Actions-compatible engine with Docker-based runners +- **Webhooks** — Outbound webhooks with event filtering and HMAC signing +- **Automations** — Rule-based workflow automation for PRs and deployments + +### AI & Quality +- **AI Code Review** — 10+ providers: GPT-4, Claude, Gemini, Groq, Ollama, OpenRouter +- **Secret Scanning** — Detect secrets in commits before they reach production +- **Branch Protection** — Required reviews, status checks, push restrictions +- **Developer Metrics** — PR velocity, review efficiency, time-to-merge tracking + +### Security +- **Authentication** — JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP, SSO/SAML +- **Authorization** — RBAC with roles, team permissions, collaborator levels +- **Rate Limiting** — Redis-backed per-endpoint rate limiting +- **Audit Logging** — Track all administrative actions ### Extensibility - -| Feature | Status | Description | -|---------|--------|-------------| -| **REST API** | Ready | 140+ API routes | -| **GraphQL** | Ready | Full GraphQL endpoint | -| **OpenAPI** | Ready | Auto-generated OpenAPI spec | -| **CLI** | Ready | 20+ command groups (`opencodehub-cli`) | -| **Storage Adapters** | Ready | 8+ backends (S3, GCS, Azure, Google Drive, etc.) | +- **REST API** — 175+ endpoints covering all platform features +- **GraphQL** — Full GraphQL endpoint for flexible queries +- **CLI** — `och` command line tool with 20+ command groups +- **Storage** — Local, S3, MinIO, R2, or any S3-compatible backend --- -## Quick Start - -### Prerequisites +## Deploy -- Node.js >= 20 -- npm or bun -- git -- PostgreSQL 14+ (recommended) or SQLite (dev) - -### Docker (Fastest) +### Docker (Recommended) ```bash -# 1. Clone git clone https://github.com/swadhinbiswas/OpencodeHub.git cd OpenCodeHub - -# 2. Configure cp .env.example .env -# Edit .env with your settings - -# 3. Start -docker-compose up -d - -# 4. Create admin user -docker-compose exec app bun run scripts/seed-admin.ts - -# 5. Open -open http://localhost:4321 +docker compose up -d +docker compose exec app bun run scripts/seed-admin.ts ``` -### NAS & Edge Deployment (Cloudflare / Tailscale) +Open **http://localhost:4321** and create your admin account. -Deploying on a home server or NAS? Use the provided `docker-compose.nas.yml` which includes `cloudflared` and `tailscale` configurations for seamless zero-trust exposure without port forwarding. +### Render (Free) -```bash -# Expose via Cloudflare Tunnel -TUNNEL_TOKEN=your_token docker-compose -f docker-compose.nas.yml --profile cloudflare up -d - -# OR Expose via Tailscale -TS_AUTHKEY=your_key docker-compose -f docker-compose.nas.yml --profile tailscale up -d -``` -See the [NAS Deployment Guide](docs/administration/deploy-nas.md) and [Exposing NAS Guide](docs/administration/expose-nas.md) for full details. - - -### Manual Setup +Deploy to Render with free PostgreSQL + Upstash Redis: ```bash -# 1. Install dependencies -npm install - -# 2. Configure environment -cp .env.example .env -# Edit .env (SQLite defaults work for dev) - -# 3. Initialize database -npm run db:push - -# 4. Create admin user -bun run scripts/seed-admin.ts - -# 5. Start development server -npm run dev +# 1. Create free Redis at upstash.com (Singapore region) +# 2. Push to GitHub +# 3. Render → New → Blueprint → Select your repo +# 4. Set REDIS_URL and SITE_URL +# 5. Deploy ``` -App URL: `http://localhost:3000` (dev) or `http://localhost:4321` (Docker) +See the [Render Deployment Guide](docs/RENDER-DEPLOYMENT.md) for step-by-step instructions. ---- +### More Deployment Options -## Architecture - -``` -Clients (Browser / Git CLI / och CLI) - | - v -OpenCodeHub Platform (Astro SSR + React) - |-- Web UI - |-- REST API (140+ routes) - |-- GraphQL Endpoint - |-- Git HTTP Server - |-- SSH Git Server (port 2222) - | - v -Persistence Layer - |-- PostgreSQL / SQLite / Turso (Drizzle ORM) - |-- Redis (sessions, queues, caching) - |-- Pluggable Storage (S3, GCS, Azure, local, etc.) -``` - -### Runtime Processes - -| Process | Command | Purpose | -|---------|---------|---------| -| Main App | `npm run dev` | Web UI + API + Git HTTP | -| SSH Git | `npm run git:start` | SSH git push/pull | -| Worker | `npm run worker:start` | Background jobs | -| Runner | `npm run runner:start` | CI/CD pipeline execution | - -### Tech Stack - -| Layer | Technology | -|-------|-----------| -| Framework | Astro 4.x (SSR, Node standalone) | -| UI | React 18 + Tailwind CSS + Radix UI | -| Database | Drizzle ORM (PostgreSQL, SQLite, Turso) | -| Auth | JWT (jose) + bcryptjs + TOTP (otplib) + OAuth (arctic) | -| Git | Native git CLI + simple-git + isomorphic-git | -| SSH | ssh2 library | -| CI/CD | Dockerode (Docker API) | -| Storage | Abstract adapter pattern (8+ backends) | -| Queue | BullMQ + Redis | -| CLI | Commander.js + Inquirer + simple-git | - ---- - -## Documentation - -Comprehensive documentation is available in the `docs/` directory: - -- **[Getting Started](docs/getting-started/installation.md)** — Installation and setup -- **[Features](docs/features/)** — Stacked PRs, AI review, merge queue, CI/CD -- **[Guides](docs/guides/)** — Team workflows, branch protection, webhooks -- **[API Reference](docs/api/rest-api.md)** — REST API documentation -- **[Administration](docs/administration/)** — Deployment, security, monitoring -- **[Development](docs/development/)** — Architecture, contributing, testing -- **[Tutorials](docs/tutorials/)** — Hands-on guides +| Platform | Guide | Cost | +|----------|-------|------| +| Docker Compose | [Deployment Guide](docs/administration/deployment.md) | Free | +| Render (Asia) | [Render Guide](docs/RENDER-DEPLOYMENT.md) | Free | +| Oracle Cloud | [Free Deployment](docs/FREE-DEPLOYMENT.md) | Free forever | +| NAS (Synology/TrueNAS) | [NAS Guide](docs/administration/deploy-nas.md) | Free | +| Kubernetes | [K8s Guide](docs/administration/kubernetes.md) | Free | +| Cloudflare Tunnel | [Cloudflare Guide](docs/administration/deploy-cloudflare.md) | Free | --- ## CLI -The companion CLI is published as `opencodehub-cli`: - ```bash # Install npm install -g opencodehub-cli -# Authenticate +# Login och auth login --url http://localhost:4321 -# Stack workflow +# Stacked PR workflow och stack create feature/auth-step-1 och stack submit och stack sync @@ -249,247 +144,114 @@ och queue add och focus ``` -[Full CLI Reference](docs/reference/cli-commands.md) +[Full CLI Reference](https://docs.opencodehub.space/reference/cli-commands/) --- ## API -OpenCodeHub provides multiple API interfaces: - -### REST API - -140+ REST endpoints covering all platform features: - ```bash -# Authentication -curl -X POST http://localhost:4321/api/auth/login \ - -H "Content-Type: application/json" \ - -d '{"login":"alice","password":"secret"}' - -# Create repository +# Create a repository curl -X POST http://localhost:4321/api/repos \ - -H "Authorization: Bearer " \ + -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"my-project","visibility":"public"}' # List pull requests -curl http://localhost:4321/api/repos/alice/my-project/pulls \ - -H "Authorization: Bearer " -``` +curl http://localhost:4321/api/repos/owner/repo/pulls \ + -H "Authorization: Bearer YOUR_TOKEN" -### GraphQL - -```bash +# GraphQL curl -X POST http://localhost:4321/api/graphql \ - -H "Authorization: Bearer " \ + -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "{ repositories { name owner { username } } }"}' ``` -### OpenAPI Spec - -The OpenAPI spec is available at: -``` -GET /api/openapi.json -``` +[Full API Reference](https://docs.opencodehub.space/api/rest-api/) --- -## Security +## Tech Stack -OpenCodeHub implements comprehensive security controls: - -- **Authentication**: JWT sessions with secure cookies, OAuth 2.0, 2FA/TOTP -- **Authorization**: Role-based access control (RBAC), repository permissions, team permissions -- **Data Protection**: AES-256-GCM encryption for workflow secrets and AI config -- **Transport**: HTTPS enforcement in production, TLS 1.2+ -- **CSRF**: Double-submit cookie pattern for state-changing requests -- **Rate Limiting**: Redis-backed per-endpoint rate limiting -- **Input Validation**: Zod schema validation on all API inputs -- **Secret Scanning**: Detect and block secrets in commits -- **SSRF Protection**: Validate webhook URLs against private IP ranges - -See [Security Best Practices](docs/administration/security.md) for detailed configuration. +| Layer | Technology | +|-------|-----------| +| Framework | Astro 4.x (SSR) + React 18 | +| UI | Tailwind CSS + Radix UI | +| Database | PostgreSQL / SQLite / Turso (Drizzle ORM) | +| Auth | JWT + OAuth + 2FA/TOTP + SSO/SAML | +| Git | Native git CLI + simple-git + isomorphic-git | +| SSH | ssh2 library | +| CI/CD | Docker-based runners, GitHub Actions syntax | +| Storage | Local filesystem or S3-compatible (AWS, MinIO, R2) | +| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter | +| Queue | BullMQ + Redis | +| CLI | Commander.js + Inquirer | --- -## Deployment - -### Docker Compose (Recommended) - -The included `docker-compose.yml` spins up: -- OpenCodeHub app (port 4321) -- PostgreSQL database -- Redis cache -- CI runner (Docker-in-Docker) -- Optional: MinIO for S3-compatible storage +## Architecture -```bash -docker-compose up -d ``` - -### Production Checklist - -Before deploying to production: - -1. **Change ALL secrets**: - ```bash - JWT_SECRET=$(openssl rand -hex 32) - SESSION_SECRET=$(openssl rand -hex 32) - INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) - CRON_SECRET=$(openssl rand -hex 32) - RUNNER_SECRET=$(openssl rand -hex 32) - WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) - ``` - -2. **Use PostgreSQL** (not SQLite) -3. **Configure HTTPS** with reverse proxy (Nginx/Caddy) -4. **Set up Redis** for distributed sessions and rate limiting -5. **Configure external storage** (S3/GCS/Azure instead of local) -6. **Enable rate limiting**: `RATE_LIMIT_ENABLED=true` -7. **Set up SMTP** for email notifications -8. **Configure backups** for PostgreSQL data - -See [Deployment Guide](docs/administration/deployment.md) for detailed instructions. - ---- - -## Database - -OpenCodeHub uses Drizzle ORM with support for multiple database engines: - -| Engine | Driver | Use Case | -|--------|--------|----------| -| **PostgreSQL** | `pg` | Production (recommended) | -| **SQLite** | `better-sqlite3` | Development, small deployments | -| **Turso/LibSQL** | `@libsql/client` | Edge deployments, Vercel | - -```bash -# Generate migrations -npm run db:generate - -# Apply migrations -npm run db:migrate - -# Push schema (dev) -npm run db:push - -# Open Drizzle Studio -npm run db:studio +┌─────────────────────────────────────────────────────────────┐ +│ CLIENTS │ +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ OPENCODEHUB PLATFORM │ +│ Web UI (Astro+React) │ REST API (175+ routes) │ GraphQL │ +│ Git Server (HTTP) │ SSH Server (ssh2) │ │ +│ Pipeline Runner (Docker) │ │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │ +└─────────────────────────────────────────────────────────────┘ ``` --- -## Storage - -All blob storage goes through an abstract `StorageAdapter`: - -| Backend | Adapter | Use Case | -|---------|---------|----------| -| **Local** | `LocalStorageAdapter` | Development, single-node | -| **S3** | `S3StorageAdapter` | Production (AWS, MinIO, R2) | -| **Google Cloud** | `GCSStorageAdapter` | GCP deployments | -| **Azure Blob** | `AzureStorageAdapter` | Azure deployments | -| **Google Drive** | `GoogleDriveStorageAdapter` | Personal/small team | -| **OneDrive** | `OneDriveStorageAdapter` | Microsoft ecosystem | -| **Dropbox** | `DropboxStorageAdapter` | Simple cloud storage | -| **Rclone** | `RcloneStorageAdapter` | Any rclone-compatible remote | +## Documentation -Configure via `STORAGE_DRIVER` environment variable. +| Topic | Link | +|-------|------| +| Installation | [docs.opencodehub.space/getting-started/installation](https://docs.opencodehub.space/getting-started/installation/) | +| Configuration | [docs.opencodehub.space/administration/configuration](https://docs.opencodehub.space/administration/configuration/) | +| Stacked PRs | [docs.opencodehub.space/features/stacked-prs](https://docs.opencodehub.space/features/stacked-prs/) | +| AI Code Review | [docs.opencodehub.space/features/ai-review](https://docs.opencodehub.space/features/ai-review/) | +| CLI Reference | [docs.opencodehub.space/reference/cli-commands](https://docs.opencodehub.space/reference/cli-commands/) | +| API Reference | [docs.opencodehub.space/api/rest-api](https://docs.opencodehub.space/api/rest-api/) | +| Deployment | [docs.opencodehub.space/administration/deployment](https://docs.opencodehub.space/administration/deployment/) | --- -## Development +## Contributing ```bash -# Setup +# Clone and setup +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpenCodeHub cp .env.example .env npm install npm run db:push bun run scripts/seed-admin.ts -# Dev server -npm run dev # Web UI + API (port 3000) -npm run git:start # SSH git server (port 2222) -npm run worker:start # Background worker -npm run runner:start # CI runner +# Start development +npm run dev -# Quality -npm run lint -npm run typecheck +# Run tests npm run test -npm run test:coverage - -# Database -npm run db:generate -npm run db:migrate -npm run db:push -npm run db:studio -``` - -See [Contributing Guide](CONTRIBUTING.md) and [Development Docs](docs/development/). - ---- - -## Project Structure - -``` -OpenCodeHub/ -├── src/ # Main application -│ ├── pages/ # Astro routes (UI + API) -│ ├── lib/ # Core business logic (120+ modules) -│ ├── db/ # Database schema and connection -│ ├── components/ # React components -│ ├── middleware.ts # Global middleware -│ └── runner/ # CI runner -├── cli/ # CLI package (opencodehub-cli) -│ └── src/commands/ # CLI command groups -├── packages/ # Additional packages -│ ├── git-rpc-daemon/ -│ ├── merge-queue-daemon/ -│ ├── ci-runner/ -│ └── sdk/ -├── docs/ # Documentation -├── docs-site/ # Documentation website -├── scripts/ # Utility scripts -├── docker-compose.yml # Docker Compose setup -├── Dockerfile # Main app container -└── package.json -``` - ---- - -## Testing - -- **Unit Tests**: Vitest (`*.test.ts`) -- **E2E Tests**: Playwright -- **Load Tests**: Custom load tests in `tests/load/` -- **Accessibility**: `@axe-core/playwright` - -```bash -npm run test # Run all tests -npm run test:coverage # Run with coverage ``` ---- - -## Contributing - -Contributions are welcome! - -1. Fork the repository -2. Create a feature branch -3. Run lint and tests -4. Open a pull request - See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and standards. --- ## Community -- **Documentation**: [docs/index.md](docs/index.md) +- **GitHub**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub) +- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space) - **Issues**: [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) - **Discussions**: [GitHub Discussions](https://github.com/swadhinbiswas/OpencodeHub/discussions) @@ -497,13 +259,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and standards. ## License -MIT. See [LICENSE](LICENSE). - ---- - -

- Ready to get started?
- Install OpenCodeHub | - Quick Start | - Learn Stacked PRs -

+[MIT](LICENSE) — Use it however you want. diff --git a/Vercel Deploy.md b/Vercel Deploy.md index 2c1cb8b9..78d74db6 100644 --- a/Vercel Deploy.md +++ b/Vercel Deploy.md @@ -1,13 +1,23 @@ -# Deploy OpenCodeHub to Vercel +# Deploy OpenCodeHub to Vercel (Edge) -> Complete guide to deploying OpenCodeHub on Vercel with Turso, Google Drive, and Redis +> Complete guide to deploying OpenCodeHub on Vercel with Turso (LibSQL), +> S3-compatible storage, and Upstash Redis. -This guide walks you through deploying OpenCodeHub to Vercel using: -- **Turso** - Serverless SQLite database (edge-compatible) -- **Google Drive API** - File storage for repositories -- **Upstash Redis** - Serverless Redis for sessions and caching +OpenCodeHub is configured entirely through environment variables, so an +edge/Serverless deployment is just a matter of pointing the built-in +database, storage, and Redis drivers at managed services. The codebase +handles all three driver families — no custom code is needed. -**Estimated time:** 30-45 minutes +- **Turso (LibSQL)** — edge-compatible SQLite database +- **S3-compatible storage** — Cloudflare R2 (free egress, recommended), + MinIO, AWS S3, Garage, etc. +- **Upstash Redis** — serverless Redis for sessions, rate limiting, queues + +> **Storage note:** only `local` and `s3` backends are supported. +> Google Drive, OneDrive, Dropbox, GCS, Azure, and FTP backends were +> removed. For Serverless, always use an S3-compatible store (`STORAGE_TYPE=s3`). + +**Estimated time:** 30-45 minutes **Cost:** Free tier available for all services --- @@ -16,7 +26,7 @@ This guide walks you through deploying OpenCodeHub to Vercel using: 1. [Prerequisites](#prerequisites) 2. [Setup Turso Database](#setup-turso-database) -3. [Setup Google Drive Storage](#setup-google-drive-storage) +3. [Setup S3-compatible Storage (R2)](#setup-s3-compatible-storage) 4. [Setup Upstash Redis](#setup-upstash-redis) 5. [Configure Project](#configure-project) 6. [Deploy to Vercel](#deploy-to-vercel) @@ -27,32 +37,24 @@ This guide walks you through deploying OpenCodeHub to Vercel using: ## Prerequisites -Before starting, ensure you have: - -- [x] GitHub account -- [x] Vercel account ([vercel.com](https://vercel.com)) -- [x] Turso account ([turso.tech](https://turso.tech)) -- [x] Google Cloud account ([console.cloud.google.com](https://console.cloud.google.com)) -- [x] Upstash account ([upstash.com](https://upstash.com)) -- [x] OpenCodeHub repository forked/cloned +- [ ] GitHub account +- [ ] Vercel account ([vercel.com](https://vercel.com)) +- [ ] Turso account ([turso.tech](https://turso.tech)) +- [ ] Cloudflare account for R2 ([cloudflare.com](https://dash.cloudflare.com)) — or any S3-compatible store +- [ ] Upstash account ([upstash.com](https://upstash.com)) +- [ ] OpenCodeHub repository forked/cloned --- ## 1. Setup Turso Database -Turso provides edge-compatible, serverless SQLite databases perfect for Vercel. +Turso provides edge-compatible, serverless SQLite databases. ### 1.1. Install Turso CLI ```bash # macOS/Linux curl -sSfL https://get.tur.so/install.sh | bash - -# Windows (PowerShell) -irm get.tur.so/install.ps1 | iex - -# Verify installation -turso --version ``` ### 1.2. Login to Turso @@ -61,8 +63,6 @@ turso --version turso auth login ``` -This opens your browser to authenticate. - ### 1.3. Create Database ```bash @@ -73,296 +73,120 @@ turso db create opencodehub turso db tokens create opencodehub # Get database URL -turso db show opencodehub --url +turso db show opencodehub ``` -**Save these values:** -```env -TURSO_DATABASE_URL=libsql://opencodehub-[your-org].turso.io -TURSO_AUTH_TOKEN=eyJhbGciOiJ... -``` +Save the **database URL** (starts with `libsql://`) and the **auth token** — +you will need them for Vercel. ### 1.4. Initialize Database Schema -```bash -# Download schema -curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/drizzle/schema.sql - -# Apply schema -turso db shell opencodehub < schema.sql +The app applies schema migrations at startup via `scripts/migrate.ts` +using the committed `drizzle/` migrations: -# Verify tables -turso db shell opencodehub "SELECT name FROM sqlite_master WHERE type='table';" +```bash +DATABASE_DRIVER=turso DATABASE_URL=libsql://... DATABASE_AUTH_TOKEN=... bun run migrate ``` -**Expected output:** -``` -users -repositories -pull_requests -pr_stacks -merge_queue_items -ai_reviews -... -``` +On Vercel, run this once from your machine against the Turso database +before the first deploy (Serverless functions have a short lifespan and +should not run migrations per-request). --- -## 2. Setup Google Drive Storage - -Google Drive API will store Git repositories and LFS files. - -### 2.1. Create Google Cloud Project - -1. Go to [Google Cloud Console](https://console.cloud.google.com) -2. Click **Select a project** → **New Project** -3. Name: `opencodehub-storage` -4. Click **Create** - -### 2.2. Enable Google Drive API - -1. In the Cloud Console, go to **APIs & Services** → **Enable APIs** -2. Search for "Google Drive API" -3. Click **Enable** +## 2. Setup S3-compatible Storage -### 2.3. Create Service Account +Use an S3-compatible object store for git objects, LFS, and artifacts. +Cloudflare R2 is recommended for Serverless because egress is free. -1. Go to **APIs & Services** → **Credentials** -2. Click **Create Credentials** → **Service Account** -3. Name: `opencodehub-storage-sa` -4. Click **Create and Continue** -5. Grant role: **Editor** -6. Click **Done** +### 2.1. Create an R2 Bucket -### 2.4. Generate Service Account Key +1. Cloudflare Dashboard → **R2** → **Create bucket** → name it `opencodehub` +2. Go to **R2 → Manage R2 API Tokens** → **Create API Token** + - Permissions: **Object Read & Write** on the bucket +3. Save the **Access Key ID** and **Secret Access Key** -1. Click on the service account you just created -2. Go to **Keys** tab -3. Click **Add Key** → **Create new key** -4. Choose **JSON** -5. Click **Create** (downloads `opencodehub-storage-xxxxx.json`) +Your endpoint is: +`https://.r2.cloudflarestorage.com` +(found in **R2 → Overview → Account ID**). -### 2.5. Create Google Drive Folder - -1. Go to [Google Drive](https://drive.google.com) -2. Create a new folder: `OpenCodeHub-Repositories` -3. Right-click → **Share** -4. Add the service account email: `opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com` -5. Grant **Editor** access -6. Copy the **Folder ID** from the URL: - ``` - https://drive.google.com/drive/folders/1a2B3c4D5e6F7g8H9i0J - ^^^^^^^^^^^^^^^^^^^^ - This is your folder ID - ``` - -### 2.6. Prepare Service Account Credentials - -Open the downloaded JSON file and extract these values: - -```json -{ - "type": "service_account", - "project_id": "opencodehub-storage", - "private_key_id": "abc123...", - "private_key": "-----BEGIN PRIVATE KEY-----\n...", - "client_email": "opencodehub-storage-sa@...", - "client_id": "123456789...", - ... -} -``` - -**Save for later:** -```env -GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0J -GOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com -GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n... -GOOGLE_CLIENT_ID=123456789... -``` +> Any S3-compatible provider works: AWS S3 (`STORAGE_ENDPOINT` empty), +> MinIO (`http://minio:9000`), Garage, SeaweedFS, Ceph RGW, Wasabi, B2. --- ## 3. Setup Upstash Redis -Upstash provides serverless Redis, perfect for Vercel edge functions. +Upstash provides serverless Redis with a REST API — ideal for Vercel. ### 3.1. Create Redis Database 1. Go to [Upstash Console](https://console.upstash.com) -2. Click **Create Database** -3. **Name:** `opencodehub-sessions` -4. **Region:** Choose closest to your users (e.g., `us-east-1`) -5. **Type:** Regional (free tier) -6. Click **Create** +2. Click **Create Database** → name it `opencodehub` +3. Region: **Global** (or closest to your Vercel region) ### 3.2. Get Connection Details -After creation, copy these values: - -**REST API:** -```env -UPSTASH_REDIS_REST_URL=https://us1-prepared-moose-12345.upstash.io -UPSTASH_REDIS_REST_TOKEN=AaBbCc...== -``` - -**Redis URL (optional):** -```env -REDIS_URL=redis://default:password@region.upstash.io:port -``` +In the database details page: -### 3.3. Configure Redis for Sessions +- **REST URL** — e.g. `https://your-redis.upstash.io` +- **REST Token** — long alphanumeric token -Upstash Redis will handle: -- User sessions -- Rate limiting -- Temporary locks -- Cache - -No additional setup needed! +Save both. --- ## 4. Configure Project -### 4.1. Update Database Configuration - -Create/update `src/db/turso.ts`: +No code changes are required — everything is configured via environment +variables that the built-in factory reads: -```typescript -import { drizzle } from 'drizzle-orm/libsql'; -import { createClient } from '@libsql/client'; +- Database: `src/db/index.ts` (reads `DATABASE_DRIVER` + `DATABASE_URL`) +- Storage: `src/lib/storage.ts` (reads `STORAGE_TYPE` + `STORAGE_*`) +- Redis: reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` + (or `REDIS_URL`) -const turso = createClient({ - url: process.env.TURSO_DATABASE_URL!, - authToken: process.env.TURSO_AUTH_TOKEN!, -}); - -export const db = drizzle(turso); -``` - -### 4.2. Update Storage Configuration - -Create/update `src/lib/storage/google-drive.ts`: - -```typescript -import { google } from 'googleapis'; - -const auth = new google.auth.GoogleAuth({ - credentials: { - client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL, - private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'), - }, - scopes: ['https://www.googleapis.com/auth/drive.file'], -}); - -const drive = google.drive({ version: 'v3', auth }); -const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID; - -export async function uploadFile(fileName: string, content: Buffer) { - const response = await drive.files.create({ - requestBody: { - name: fileName, - parents: [folderId!], - }, - media: { - body: content, - }, - }); - - return response.data.id; -} - -export async function downloadFile(fileId: string) { - const response = await drive.files.get( - { fileId, alt: 'media' }, - { responseType: 'arraybuffer' } - ); - - return Buffer.from(response.data as ArrayBuffer); -} - -export async function deleteFile(fileId: string) { - await drive.files.delete({ fileId }); -} -``` - -### 4.3. Update Redis Configuration - -Create/update `src/lib/redis.ts`: - -```typescript -import { Redis } from '@upstash/redis'; - -export const redis = new Redis({ - url: process.env.UPSTASH_REDIS_REST_URL!, - token: process.env.UPSTASH_REDIS_REST_TOKEN!, -}); - -// Session management -export async function setSession(sessionId: string, data: any, ttl = 7 * 24 * 60 * 60) { - await redis.setex(sessionId, ttl, JSON.stringify(data)); -} - -export async function getSession(sessionId: string) { - const data = await redis.get(sessionId); - return data ? JSON.parse(data as string) : null; -} - -export async function deleteSession(sessionId: string) { - await redis.del(sessionId); -} -``` - -### 4.4. Install Dependencies - -```bash -# Add Turso client -bun add @libsql/client drizzle-orm - -# Add Google Drive API -bun add googleapis - -# Add Upstash Redis -bun add @upstash/redis -``` - -### 4.5. Update Environment Variables Template - -Create `.env.example`: +### 4.1. Environment Variables Template ```env -# === Turso Database === -TURSO_DATABASE_URL=libsql://your-db.turso.io -TURSO_AUTH_TOKEN=your-token - -# === Google Drive Storage === -GOOGLE_DRIVE_FOLDER_ID=your-folder-id -GOOGLE_SERVICE_ACCOUNT_EMAIL=your-sa@project.iam.gserviceaccount.com -GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" -GOOGLE_CLIENT_ID=your-client-id - -# === Upstash Redis === +# === Database (Turso) === +DATABASE_DRIVER=turso +DATABASE_URL=libsql://your-db.turso.io +DATABASE_AUTH_TOKEN=your-token + +# === Storage (S3-compatible — R2 shown) === +STORAGE_TYPE=s3 +STORAGE_BUCKET=opencodehub +STORAGE_REGION=auto +STORAGE_ENDPOINT=https://.r2.cloudflarestorage.com +STORAGE_ACCESS_KEY_ID=your-access-key-id +STORAGE_SECRET_ACCESS_KEY=your-secret-access-key + +# === Redis (Upstash) === UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io UPSTASH_REDIS_REST_TOKEN=your-token # === Application === SITE_URL=https://your-app.vercel.app +NODE_ENV=production JWT_SECRET=generate-a-strong-secret SESSION_SECRET=generate-another-strong-secret INTERNAL_HOOK_SECRET=generate-hook-secret +CRON_SECRET=generate-cron-secret +RUNNER_SECRET=generate-runner-secret +AI_CONFIG_ENCRYPTION_KEY=generate-encryption-key +WORKFLOW_SECRET_ENCRYPTION_KEY=generate-encryption-key +METRICS_TOKEN=generate-metrics-token # === Optional: AI Review === AI_PROVIDER=openai OPENAI_API_KEY=sk-... ``` -### 4.6. Generate Secrets +### 4.2. Generate Secrets ```bash -# Generate secrets -openssl rand -base64 32 # JWT_SECRET -openssl rand -base64 32 # SESSION_SECRET -openssl rand -base64 32 # INTERNAL_HOOK_SECRET +openssl rand -base64 32 # each secret ``` --- @@ -382,163 +206,61 @@ vercel login 1. Go to [Vercel Dashboard](https://vercel.com/dashboard) 2. Click **Add New** → **Project** -3. Import your GitHub repository -4. Select `OpencodeHub` +3. Import your OpenCodeHub repository **Option B: Via CLI** ```bash -cd opencodehub -vercel +vercel --prod ``` ### 5.3. Configure Environment Variables -In Vercel Dashboard: - -1. Go to **Project Settings** → **Environment Variables** -2. Add all variables from step 4.5: - -**Turso:** -- `TURSO_DATABASE_URL` = `libsql://opencodehub-xxxx.turso.io` -- `TURSO_AUTH_TOKEN` = `eyJhbG...` - -**Google Drive:** -- `GOOGLE_DRIVE_FOLDER_ID` = `1a2B3c4D5e6F7g8H9i0J` -- `GOOGLE_SERVICE_ACCOUNT_EMAIL` = `opencodehub-storage-sa@...` -- `GOOGLE_PRIVATE_KEY` = Paste entire private key (including `\n`) -- `GOOGLE_CLIENT_ID` = `123456789...` - -**Upstash Redis:** -- `UPSTASH_REDIS_REST_URL` = `https://...upstash.io` -- `UPSTASH_REDIS_REST_TOKEN` = `AaBbCc...` - -**Application:** -- `SITE_URL` = `https://your-project.vercel.app` (update later) -- `JWT_SECRET` = Generated secret -- `SESSION_SECRET` = Generated secret -- `INTERNAL_HOOK_SECRET` = Generated secret - -**AI (Optional):** -- `AI_PROVIDER` = `openai` -- `OPENAI_API_KEY` = `sk-...` +In **Vercel → Project → Settings → Environment Variables**, add every +variable from section 4.1. ### 5.4. Configure Build Settings -In **Project Settings** → **Build & Development Settings**: +Use the defaults (Vercel auto-detects Astro): -- **Framework Preset:** Astro -- **Build Command:** `bun run build` -- **Output Directory:** `dist` -- **Install Command:** `bun install` +- **Build Command:** `npm run build` +- **Output Directory:** `dist/` +- **Framework:** Astro -### 5.5. Deploy! +> The repo ships `@astrojs/vercel` and a `vercel.json`. For edge use, +> switch the adapter in `astro.config.mjs` from `@astrojs/node` to +> `@astrojs/vercel` with `output: "server"` (Turso + Upstash are both +> edge-compatible). -Click **Deploy** or run: +### 5.5. Deploy! ```bash vercel --prod ``` -**Deployment will:** -1. Install dependencies -2. Build the project -3. Deploy to Vercel Edge Network -4. Provide deployment URL - --- ## 6. Post-Deployment ### 6.1. Update SITE_URL -1. Copy your Vercel deployment URL: `https://opencodehub-abc123.vercel.app` -2. Update environment variable: - - Go to **Settings** → **Environment Variables** - - Edit `SITE_URL` = your actual URL -3. **Redeploy** (Vercel Dashboard → Deployments → Redeploy) +Set `SITE_URL` to your final domain, e.g. `https://git.yourdomain.com`. ### 6.2. Create Admin User -**Option A: Via Turso CLI** - -```bash -turso db shell opencodehub - --- In the Turso shell: -INSERT INTO users (id, email, password_hash, username, role, created_at) -VALUES ( - 'admin-user-id', - 'admin@yourcompany.com', - '$2b$10$...', -- bcrypt hash of your password - 'admin', - 'admin', - current_timestamp -); -``` - -**Generate bcrypt hash:** -```javascript -// Run in Node.js -const bcrypt = require('bcrypt'); -const hash = await bcrypt.hash('your-password', 10); -console.log(hash); -``` - -**Option B: Via Deployment Script** - -Create `scripts/create-admin-vercel.ts`: - -```typescript -import { db } from '../src/db/turso'; -import { users } from '../src/db/schema'; -import bcrypt from 'bcrypt'; - -const email = process.env.ADMIN_EMAIL || 'admin@example.com'; -const password = process.env.ADMIN_PASSWORD || 'changeme'; - -const hash = await bcrypt.hash(password, 10); - -await db.insert(users).values({ - id: crypto.randomUUID(), - email, - username: 'admin', - password_hash: hash, - role: 'admin', - createdAt: new Date(), -}); - -console.log(`✅ Admin user created: ${email}`); -``` - -Run once: ```bash -ADMIN_EMAIL=your@email.com ADMIN_PASSWORD=strong123 bun scripts/create-admin-vercel.ts +# From your machine, once, against the same Turso database: +DATABASE_DRIVER=turso DATABASE_URL=libsql://... DATABASE_AUTH_TOKEN=... bun run scripts/seed-admin.ts ``` ### 6.3. Configure Custom Domain (Optional) -1. Go to **Settings** → **Domains** -2. Add your custom domain: `git.yourcompany.com` -3. Follow DNS configuration instructions -4. Update `SITE_URL` to your custom domain +In Vercel → Project → **Settings → Domains**, add your domain. ### 6.4. Setup Monitoring -**Vercel Analytics:** -1. Go to **Analytics** tab -2. Enable **Web Analytics** -3. Enable **Speed Insights** - -**Sentry (Optional):** -```bash -bun add @sentry/astro -``` - -Add to `.env`: -```env -SENTRY_DSN=https://...@sentry.io/... -``` +- Prometheus: scrape `GET /api/metrics` with `Authorization: Bearer $METRICS_TOKEN` +- Use Vercel's built-in analytics + logs for request/error tracking --- @@ -546,249 +268,68 @@ SENTRY_DSN=https://...@sentry.io/... ### 7.1. Test Database Connection -Visit: `https://your-app.vercel.app/api/health` - -Should return: -```json -{ - "status": "ok", - "database": "connected", - "redis": "connected", - "storage": "configured" -} +```bash +turso db shell opencodehub +# Run: SELECT count(*) FROM users; ``` ### 7.2. Test Storage -1. Login as admin -2. Create a repository -3. Push a commit -4. Verify file appears in Google Drive folder +1. Create a repository with a large file (LFS) +2. Push — verify the object appears in the R2 bucket ### 7.3. Test Redis -1. Login -2. Check session persists -3. Logout -4. Session cleared +Push a git update and confirm webhooks/queues process (Redis-backed). ### 7.4. Load Test ```bash # Install hey -go install github.com/rakyll/hey@latest +brew install hey # or: go install github.com/rakyll/hey@latest # Test -hey -n 1000 -c 10 https://your-app.vercel.app +hey -n 1000 -c 50 https://your-app.vercel.app/api/health ``` -Expected: >90% success rate - --- ## 8. Troubleshooting ### Database Connection Fails -**Error:** `Failed to connect to Turso` - -**Solution:** -```bash -# Verify token is valid -turso db tokens validate opencodehub YOUR_TOKEN - -# Regenerate if needed -turso db tokens create opencodehub -``` - -Update environment variable and redeploy. +- Verify `DATABASE_DRIVER=turso` and the `libsql://` URL + token are correct +- Run `bun run migrate` locally against the same DB (see section 1.4) -### Google Drive Upload Fails +### Storage Upload Fails -**Error:** `Insufficient permissions` - -**Solution:** -1. Verify service account has Editor access to folder -2. Check private key doesn't have extra spaces/newlines -3. Test locally: - ```bash - bun test src/lib/storage/google-drive.test.ts - ``` +- Verify bucket name, endpoint, and access key (S3 v4 signature) +- R2: token must have **Object Read & Write** scope on the bucket ### Redis Connection Timeout -**Error:** `ETIMEDOUT connecting to Redis` - -**Solution:** -1. Verify Upstash Redis region matches Vercel deployment region -2. Check REST URL and token are correct -3. Test connection: - ```bash - curl https://YOUR_REDIS_URL/get/test \ - -H "Authorization: Bearer YOUR_TOKEN" - ``` +- Confirm the Upstash REST URL/token are set (not `REDIS_URL`) ### Build Fails on Vercel -**Error:** `Command "build" exited with 1` - -**Solution:** ```bash # Test build locally -bun run build +npm run build # Check for TypeScript errors -bun run typecheck +npm run typecheck # Check environment variables are set -vercel env ls -``` - -### Out of Memory - -**Error:** `JavaScript heap out of memory` - -** Solution:** -Upgrade Vercel plan or optimize build: - -```json -// package.json -{ - "scripts": { - "build": "NODE_OPTIONS=--max_old_space_size=4096 astro build" - } -} +bun run src/lib/env-validation.ts ``` --- -## Cost Estimation - -### Free Tier Limits - -**Turso:** -- Database: Free up to 8 GB -- Rows: 500M -- Locations: 1 primary + 2 replicas - -**Upstash Redis:** -- Commands: 10,000/day -- Storage: 256 MB -- Bandwidth: 256 MB/month - -**Google Drive:** -- Storage: 15 GB free -- API calls: Unlimited - -**Vercel:** -- Bandwidth: 100 GB/month -- Deployments: Unlimited -- Edge Functions: 100 GB-hrs - -### Estimated Monthly Cost - -**Low traffic (< 1,000 users):** -- All free tier ✅ - -**Medium traffic (1,000-10,000 users):** -- Turso: $0 (within limits) -- Upstash: $10/month (higher tier) -- Google Drive: $2/month (Google One 100GB) -- Vercel: $20/month (Pro plan) -**Total: ~$32/month** - -**High traffic (>10,000 users):** -- Turso: $29/month (Pro) -- Upstash: $50/month (Pro) -- Google Drive: $10/month (Google Workspace) -- Vercel: $20/month (Pro) -**Total: ~$109/month** - ---- - -## Performance Optimization - -### Edge Caching - -Add to `vercel.json`: - -```json -{ - "headers": [ - { - "source": "/assets/(.*)", - "headers": [ - { - "key": "Cache-Control", - "value": "public, max-age=31536000, immutable" - } - ] - } - ] -} -``` - -### Database Connection Pooling - -Turso handles this automatically with libSQL. - -### Redis Caching Strategy - -```typescript -// Cache expensive queries -async function getRepository(id: string) { - const cached = await redis.get(`repo:${id}`); - if (cached) return JSON.parse(cached as string); - - const repo = await db.query.repositories.findFirst({ where: eq(repositories.id, id) }); - await redis.setex(`repo:${id}`, 300, JSON.stringify(repo)); // Cache 5 min - - return repo; -} -``` - ---- - -## Security Checklist - -- [ ] All secrets in environment variables (not committed to Git) -- [ ] HTTPS enabled (automatic on Vercel) -- [ ] Database access restricted to Vercel IPs -- [ ] Google Drive folder shared only with service account -- [ ] Redis requires authentication -- [ ] Rate limiting enabled -- [ ] CORS configured properly -- [ ] CSP headers set - ---- - -## Next Steps - -1. **Setup CI/CD**: Automatic deployments on push to main -2. **Add Monitoring**: Set up Sentry for error tracking -3. **Configure Backups**: Turso auto-backups + manual exports -4. **Scale Redis**: Upgrade to Global for multi-region -5. **Add CDN**: Use Vercel Edge Network or Cloudflare - ---- - -## Additional Resources - -- [Turso Documentation](https://docs.turso.tech) -- [Google Drive API Reference](https://developers.google.com/drive/api/v3/reference) -- [Upstash Redis Docs](https://docs.upstash.com/redis) -- [Vercel Deployment Docs](https://vercel.com/docs) -- [OpenCodeHub GitHub](https://github.com/swadhinbiswas/OpencodeHub) - ---- - -## Support - -**Need help?** -- 💬 [Discord Community](https://discord.gg/opencodehub) -- 🐛 [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) -- 📧 [Email Support](mailto:support@opencodehub.com) - ---- +## Limitations vs. Self-Hosted -**Congratulations!** 🎉 Your OpenCodeHub instance is now running on Vercel with world-class serverless infrastructure! +- **No CI/CD runner** — pipeline execution requires a Docker host; on Vercel + use a self-hosted runner (see `packages/ci-runner` / `Dockerfile.runner`) +- **No SSH Git** — SSH git requires a long-lived process + (`scripts/ssh-server.ts`); use HTTP git with PATs on Serverless +- **Background worker** — scheduled jobs (`scripts/worker.ts`) need a + long-running host; run it in a small container next to your DB diff --git a/astro.config.mjs b/astro.config.mjs index 0bcf7953..318f2e0a 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,24 +1,35 @@ import node from "@astrojs/node"; import react from "@astrojs/react"; -import tailwind from "@astrojs/tailwind"; import { defineConfig } from "astro/config"; +import icon from "astro-icon"; + +import { config } from "dotenv"; +config(); +// Load environment variables safely +const port = parseInt(process.env.PORT || "4321"); export default defineConfig({ integrations: [ react(), - tailwind({ - applyBaseStyles: false, - }), + icon(), ], output: "server", adapter: node({ mode: "standalone", }), server: { - port: parseInt(process.env.PORT || "4321"), + port, host: true, }, vite: { + css: { + transformer: 'postcss', + }, + server: { + watch: { + ignored: ['**/data/**', '**/repos/**', '**/storage/**', '**/.tmp/**', '**/postgres/**', '**/redis/**'], + }, + }, optimizeDeps: { exclude: ["nodegit", "better-sqlite3"], }, diff --git a/bun.lock b/bun.lock index 5cb35682..c50ea4cf 100644 --- a/bun.lock +++ b/bun.lock @@ -8,7 +8,6 @@ "@astrojs/check": "^0.9.6", "@astrojs/node": "^8.3.0", "@astrojs/react": "^3.6.0", - "@astrojs/tailwind": "^5.1.0", "@astrojs/vercel": "^7.7.0", "@aws-sdk/client-s3": "^3.948.0", "@aws-sdk/lib-storage": "^3.972.0", @@ -16,7 +15,11 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@google/genai": "^2.11.0", + "@iconify-json/file-icons": "^1.2.2", + "@iconify-json/vscode-icons": "^1.2.67", "@libsql/client": "^0.15.15", + "@mistralai/mistralai": "^2.4.1", "@monaco-editor/react": "^4.6.0", "@opentelemetry/auto-instrumentations-node": "^0.76.0", "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", @@ -51,6 +54,8 @@ "@upstash/redis": "^1.36.0", "arctic": "^3.7.0", "astro": "^4.11.0", + "astro-icon": "^1.1.5", + "autoprefixer": "^10.4.19", "bcryptjs": "^2.4.3", "better-sqlite3": "^11.1.2", "bullmq": "^5.65.1", @@ -60,11 +65,13 @@ "cobe": "^0.6.5", "date-fns": "^4.1.0", "dockerode": "^4.0.12", + "dotenv": "^17.4.2", "drizzle-orm": "^0.45.2", "framer-motion": "^11.18.2", "github-markdown-css": "^5.8.1", "graphql": "^16.12.0", "graphql-yoga": "^5.18.0", + "highlight.js": "^11.11.1", "ioredis": "^5.9.2", "isomorphic-dompurify": "^2.35.0", "isomorphic-git": "^1.25.10", @@ -86,6 +93,7 @@ "pg": "^8.16.3", "pino": "^10.1.0", "pino-loki": "^3.0.0", + "postcss": "^8.5.2", "postgres": "^3.4.7", "prom-client": "^15.1.3", "qrcode": "^1.5.4", @@ -93,6 +101,7 @@ "react-dom": "^18.3.1", "react-globe.gl": "^2.37.0", "recharts": "^3.7.0", + "rehype-highlight": "^7.0.2", "rehype-sanitize": "^6.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", @@ -103,6 +112,7 @@ "sonner": "^2.0.7", "ssh2": "^1.15.0", "tailwind-merge": "^2.6.0", + "tailwindcss": "^3.4.4", "tailwindcss-animate": "^1.0.7", "three": "^0.182.0", "unified": "^11.0.5", @@ -129,12 +139,10 @@ "@types/ssh2": "^1.15.0", "@types/three": "^0.182.0", "@vitest/coverage-v8": "^4.1.5", - "autoprefixer": "^10.4.19", "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3", - "postcss": "^8.5.10", - "tailwindcss": "^3.4.4", "typescript": "^5.9.3", + "vite": "^5.4.11", "vitest": "^4.1.5", }, }, @@ -170,11 +178,11 @@ }, }, "overrides": { - "brace-expansion": "^2.1.1", + "brace-expansion": "^2.0.1", "cross-spawn": "^7.0.6", "devalue": "^5.8.1", "diff": "^5.2.0", - "fast-uri": "^3.1.2", + "fast-uri": "^3.0.6", "fast-xml-builder": "^1.2.0", "fast-xml-parser": "^5.8.0", "lodash": "^4.17.23", @@ -185,15 +193,18 @@ "protobufjs": "^7.6.0", "qs": "^6.15.0", "rollup": "^4.61.0", - "tar": "^7.5.16", + "tar": "^7.5.8", "ws": "^8.18.0", - "yaml": "^2.8.0", }, "packages": { "@acemir/cssom": ["@acemir/cssom@0.9.31", "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.31.tgz", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + + "@antfu/utils": ["@antfu/utils@8.1.1", "https://registry.npmmirror.com/@antfu/utils/-/utils-8.1.1.tgz", {}, "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="], + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.11", "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="], "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.8.1", "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.6" } }, "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ=="], @@ -218,8 +229,6 @@ "@astrojs/react": ["@astrojs/react@3.6.3", "https://registry.npmmirror.com/@astrojs/react/-/react-3.6.3.tgz", { "dependencies": { "@vitejs/plugin-react": "^4.3.3", "ultrahtml": "^1.5.3", "vite": "^5.4.10" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21", "@types/react-dom": "^17.0.17 || ^18.0.6", "react": "^17.0.2 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0-beta" } }, "sha512-5ihLQDH5Runddug5AZYlnp/Q5T81QxhwnWJXA9rchBAdh11c6UhBbv9Kdk7b2PkXoEU70CGWBP9hSh0VCR58eA=="], - "@astrojs/tailwind": ["@astrojs/tailwind@5.1.5", "https://registry.npmmirror.com/@astrojs/tailwind/-/tailwind-5.1.5.tgz", { "dependencies": { "autoprefixer": "^10.4.20", "postcss": "^8.5.1", "postcss-load-config": "^4.0.2" }, "peerDependencies": { "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", "tailwindcss": "^3.0.24" } }, "sha512-1diguZEau7FZ9vIjzE4BwavGdhD3+JkdS8zmibl1ene+EHgIU5hI0NMgRYG3yea+Niaf7cyMwjeWeLvzq/maxg=="], - "@astrojs/telemetry": ["@astrojs/telemetry@3.1.0", "https://registry.npmmirror.com/@astrojs/telemetry/-/telemetry-3.1.0.tgz", { "dependencies": { "ci-info": "^4.0.0", "debug": "^4.3.4", "dlv": "^1.1.3", "dset": "^3.1.3", "is-docker": "^3.0.0", "is-wsl": "^3.0.0", "which-pm-runs": "^1.1.0" } }, "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA=="], "@astrojs/vercel": ["@astrojs/vercel@7.8.2", "https://registry.npmmirror.com/@astrojs/vercel/-/vercel-7.8.2.tgz", { "dependencies": { "@astrojs/internal-helpers": "^0.4.1", "@vercel/analytics": "^1.3.1", "@vercel/edge": "^1.1.2", "@vercel/nft": "^0.27.4", "esbuild": "^0.21.5", "fast-glob": "^3.3.2", "web-vitals": "^3.5.2" }, "peerDependencies": { "astro": "^4.2.0" } }, "sha512-U2JsfN0LzFMX5CPrcb+5bAAyqyYURP6Dk/mUIXX87r5x4baQy+juU+ntvf926YA0tR7u6jPRXHymE2axQ/l3NQ=="], @@ -468,6 +477,8 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + "@google/genai": ["@google/genai@2.11.0", "https://registry.npmmirror.com/@google/genai/-/genai-2.11.0.tgz", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-d2Csf29vS0GfHc52H0MG25ccY4FKvvbDgqDlEovLrPLF8sPegWr/GGO+LMOy85/1SnX0iV0zDAW7R8SsvWg8Vg=="], + "@graphql-tools/executor": ["@graphql-tools/executor@1.5.3", "https://registry.npmmirror.com/@graphql-tools/executor/-/executor-1.5.3.tgz", { "dependencies": { "@graphql-tools/utils": "^11.1.0", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA=="], "@graphql-tools/merge": ["@graphql-tools/merge@9.1.9", "https://registry.npmmirror.com/@graphql-tools/merge/-/merge-9.1.9.tgz", { "dependencies": { "@graphql-tools/utils": "^11.1.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-iHUWNjRHeQRYdgIMIuChThOwoKzA9vrzYeslgfBo5eUYEyHGZCoDPjAavssoYXLwstYt1dZj2J22jSzc2DrN0Q=="], @@ -488,6 +499,16 @@ "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "https://registry.npmmirror.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + "@iconify-json/file-icons": ["@iconify-json/file-icons@1.2.2", "https://registry.npmmirror.com/@iconify-json/file-icons/-/file-icons-1.2.2.tgz", { "dependencies": { "@iconify/types": "*" } }, "sha512-ajk44wYGTiu79EAyrfNHNaxZ1/2Z1xuSOAvYUT/pAPWHc+P6n9TUZP/ccnPG18kx0WMBxmkHHQ9/zkm6ENhk+Q=="], + + "@iconify-json/vscode-icons": ["@iconify-json/vscode-icons@1.2.67", "https://registry.npmmirror.com/@iconify-json/vscode-icons/-/vscode-icons-1.2.67.tgz", { "dependencies": { "@iconify/types": "*" } }, "sha512-/fObxEkBtqK2e1qf1IRjuZ4drWknAnzykdT2ngGAKESf35tCcj0Lx3MdsxYBneE5YbgvsUfOl0hbZ59R5s19vw=="], + + "@iconify/tools": ["@iconify/tools@4.2.0", "https://registry.npmmirror.com/@iconify/tools/-/tools-4.2.0.tgz", { "dependencies": { "@iconify/types": "^2.0.0", "@iconify/utils": "^2.3.0", "cheerio": "^1.1.2", "domhandler": "^5.0.3", "extract-zip": "^2.0.1", "local-pkg": "^1.1.2", "pathe": "^2.0.3", "svgo": "^3.3.2", "tar": "^7.5.2" } }, "sha512-WRxPva/ipxYkqZd1+CkEAQmd86dQmrwH0vwK89gmp2Kh2WyyVw57XbPng0NehP3x4V1LzLsXUneP1uMfTMZmUA=="], + + "@iconify/types": ["@iconify/types@2.0.0", "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@2.3.0", "https://registry.npmmirror.com/@iconify/utils/-/utils-2.3.0.tgz", { "dependencies": { "@antfu/install-pkg": "^1.0.0", "@antfu/utils": "^8.1.0", "@iconify/types": "^2.0.0", "debug": "^4.4.0", "globals": "^15.14.0", "kolorist": "^1.8.0", "local-pkg": "^1.0.0", "mlly": "^1.7.4" } }, "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], @@ -584,6 +605,8 @@ "@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.3", "https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg=="], + "@mistralai/mistralai": ["@mistralai/mistralai@2.4.1", "https://registry.npmmirror.com/@mistralai/mistralai/-/mistralai-2.4.1.tgz", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-vNpR2GR76aW7fK8Dcu1NRx3fdSPO1QruRDo4GbfXfkro2mvp194xIAvJ2BP4ZykzyWtJoDmBe+Qsk9D443aqlg=="], + "@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "https://registry.npmmirror.com/@monaco-editor/loader/-/loader-1.7.0.tgz", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="], "@monaco-editor/react": ["@monaco-editor/react@4.7.0", "https://registry.npmmirror.com/@monaco-editor/react/-/react-4.7.0.tgz", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="], @@ -1166,6 +1189,8 @@ "@types/react-dom": ["@types/react-dom@18.3.7", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], + "@types/retry": ["@types/retry@0.12.0", "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], + "@types/ssh2": ["@types/ssh2@1.15.5", "https://registry.npmmirror.com/@types/ssh2/-/ssh2-1.15.5.tgz", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="], "@types/stats.js": ["@types/stats.js@0.17.4", "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="], @@ -1188,6 +1213,8 @@ "@types/ws": ["@types/ws@8.18.1", "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@types/yauzl": ["@types/yauzl@2.10.3", "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], "@upstash/redis": ["@upstash/redis@1.38.0", "https://registry.npmmirror.com/@upstash/redis/-/redis-1.38.0.tgz", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg=="], @@ -1296,6 +1323,8 @@ "astro": ["astro@4.16.19", "https://registry.npmmirror.com/astro/-/astro-4.16.19.tgz", { "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/internal-helpers": "0.4.1", "@astrojs/markdown-remark": "5.3.0", "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/types": "^7.26.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.3", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.1.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^0.7.2", "cssesc": "^3.0.0", "debug": "^4.3.7", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.5.4", "esbuild": "^0.21.5", "estree-walker": "^3.0.3", "fast-glob": "^3.3.2", "flattie": "^1.1.1", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "html-escaper": "^3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.14", "magicast": "^0.3.5", "micromatch": "^4.0.8", "mrmime": "^2.0.0", "neotraverse": "^0.6.18", "ora": "^8.1.1", "p-limit": "^6.1.0", "p-queue": "^8.0.1", "preferred-pm": "^4.0.0", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.6.3", "shiki": "^1.23.1", "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "vite": "^5.4.11", "vitefu": "^1.0.4", "which-pm": "^3.0.0", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-baeSswPC5ZYvhGDoj25L2FuzKRWMgx105FetOPQVJFMCAp0o08OonYC7AhwsFdhvp7GapqjnC1Fe3lKb2lupYw=="], + "astro-icon": ["astro-icon@1.1.5", "https://registry.npmmirror.com/astro-icon/-/astro-icon-1.1.5.tgz", { "dependencies": { "@iconify/tools": "^4.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^2.1.30" } }, "sha512-CJYS5nWOw9jz4RpGWmzNQY7D0y2ZZacH7atL2K9DeJXJVaz7/5WrxeyIxO8KASk1jCM96Q4LjRx/F3R+InjJrw=="], + "async-lock": ["async-lock@1.4.1", "https://registry.npmmirror.com/async-lock/-/async-lock-1.4.1.tgz", {}, "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="], "async-sema": ["async-sema@3.1.1", "https://registry.npmmirror.com/async-sema/-/async-sema-3.1.1.tgz", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="], @@ -1356,6 +1385,8 @@ "bl": ["bl@4.1.0", "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "boolbase": ["boolbase@1.0.0", "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + "bowser": ["bowser@2.14.1", "https://registry.npmmirror.com/bowser/-/bowser-2.14.1.tgz", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="], "boxen": ["boxen@7.1.1", "https://registry.npmmirror.com/boxen/-/boxen-7.1.1.tgz", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.1", "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.1.0" } }, "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog=="], @@ -1368,6 +1399,10 @@ "buffer": ["buffer@5.6.0", "https://registry.npmmirror.com/buffer/-/buffer-5.6.0.tgz", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="], + "buffer-crc32": ["buffer-crc32@0.2.13", "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + "buffer-from": ["buffer-from@1.1.2", "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], "buildcheck": ["buildcheck@0.0.7", "https://registry.npmmirror.com/buildcheck/-/buildcheck-0.0.7.tgz", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="], @@ -1408,7 +1443,11 @@ "chardet": ["chardet@2.1.1", "https://registry.npmmirror.com/chardet/-/chardet-2.1.1.tgz", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], - "chokidar": ["chokidar@3.6.0", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + "cheerio": ["cheerio@1.2.0", "https://registry.npmmirror.com/cheerio/-/cheerio-1.2.0.tgz", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], + + "cheerio-select": ["cheerio-select@2.1.0", "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], + + "chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chownr": ["chownr@1.1.4", "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], @@ -1462,6 +1501,8 @@ "conf": ["conf@12.0.0", "https://registry.npmmirror.com/conf/-/conf-12.0.0.tgz", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "atomically": "^2.0.2", "debounce-fn": "^5.1.2", "dot-prop": "^8.0.2", "env-paths": "^3.0.0", "json-schema-typed": "^8.0.1", "semver": "^7.5.4", "uint8array-extras": "^0.3.0" } }, "sha512-fIWyWUXrJ45cHCIQX+Ck1hrZDIf/9DR0P0Zewn3uNht28hbt5OfGUq8rRWsxi96pZWPyBEd0eY9ama01JTaknA=="], + "confbox": ["confbox@0.2.4", "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], + "consola": ["consola@3.4.2", "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "convert-source-map": ["convert-source-map@2.0.0", "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], @@ -1478,10 +1519,16 @@ "cross-spawn": ["cross-spawn@7.0.6", "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "css-select": ["css-select@5.2.2", "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], + "css-tree": ["css-tree@3.2.1", "https://registry.npmmirror.com/css-tree/-/css-tree-3.2.1.tgz", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], + "css-what": ["css-what@6.2.2", "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], + "cssesc": ["cssesc@3.0.0", "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + "csso": ["csso@5.0.5", "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], + "cssstyle": ["cssstyle@6.2.0", "https://registry.npmmirror.com/cssstyle/-/cssstyle-6.2.0.tgz", { "dependencies": { "@asamuzakjp/css-color": "^5.0.1", "@csstools/css-syntax-patches-for-csstree": "^1.0.28", "css-tree": "^3.1.0", "lru-cache": "^11.2.6" } }, "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig=="], "csstype": ["csstype@3.2.3", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], @@ -1562,7 +1609,7 @@ "destroy": ["destroy@1.2.0", "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], - "detect-libc": ["detect-libc@2.0.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], + "detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "detect-node-es": ["detect-node-es@1.1.0", "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], @@ -1586,10 +1633,20 @@ "dockerode": ["dockerode@4.0.12", "https://registry.npmmirror.com/dockerode/-/dockerode-4.0.12.tgz", { "dependencies": { "@balena/dockerignore": "^1.0.2", "@grpc/grpc-js": "^1.11.1", "@grpc/proto-loader": "^0.7.13", "docker-modem": "^5.0.7", "protobufjs": "^7.3.2", "tar-fs": "^2.1.4", "uuid": "^10.0.0" } }, "sha512-/bCZd6KlGcjZO8Buqmi/vXuqEGVEZ0PNjx/biBNqJD3MhK9DmdiAuKxqfNhflgDESDIiBz3qF+0e55+CpnrUcw=="], + "dom-serializer": ["dom-serializer@2.0.0", "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], + + "domelementtype": ["domelementtype@2.3.0", "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], + + "domhandler": ["domhandler@5.0.3", "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], + "dompurify": ["dompurify@3.4.8", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.8.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ=="], + "domutils": ["domutils@3.2.2", "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], + "dot-prop": ["dot-prop@8.0.2", "https://registry.npmmirror.com/dot-prop/-/dot-prop-8.0.2.tgz", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="], + "dotenv": ["dotenv@17.4.2", "https://registry.npmmirror.com/dotenv/-/dotenv-17.4.2.tgz", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + "drizzle-kit": ["drizzle-kit@0.31.10", "https://registry.npmmirror.com/drizzle-kit/-/drizzle-kit-0.31.10.tgz", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "tsx": "^4.21.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw=="], "drizzle-orm": ["drizzle-orm@0.45.2", "https://registry.npmmirror.com/drizzle-orm/-/drizzle-orm-0.45.2.tgz", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q=="], @@ -1602,6 +1659,8 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "ee-first": ["ee-first@1.1.1", "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "electron-to-chromium": ["electron-to-chromium@1.5.366", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.366.tgz", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="], @@ -1616,6 +1675,8 @@ "encoding": ["encoding@0.1.13", "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + "encoding-sniffer": ["encoding-sniffer@0.2.1", "https://registry.npmmirror.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], + "end-of-stream": ["end-of-stream@1.4.5", "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "entities": ["entities@8.0.0", "https://registry.npmmirror.com/entities/-/entities-8.0.0.tgz", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="], @@ -1662,10 +1723,14 @@ "exponential-backoff": ["exponential-backoff@3.1.3", "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], + "exsolve": ["exsolve@1.1.0", "https://registry.npmmirror.com/exsolve/-/exsolve-1.1.0.tgz", {}, "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw=="], + "extend": ["extend@3.0.2", "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "extend-shallow": ["extend-shallow@2.0.1", "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + "extract-zip": ["extract-zip@2.0.1", "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + "fast-copy": ["fast-copy@4.0.3", "https://registry.npmmirror.com/fast-copy/-/fast-copy-4.0.3.tgz", {}, "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -1684,6 +1749,8 @@ "fastq": ["fastq@1.20.1", "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + "fd-slicer": ["fd-slicer@1.1.0", "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], + "fdir": ["fdir@6.5.0", "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fetch-blob": ["fetch-blob@3.2.0", "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], @@ -1754,7 +1821,7 @@ "get-proto": ["get-proto@1.0.1", "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-stream": ["get-stream@9.0.1", "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + "get-stream": ["get-stream@5.2.0", "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], "get-tsconfig": ["get-tsconfig@4.14.0", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], @@ -1768,8 +1835,12 @@ "glob-parent": ["glob-parent@6.0.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + "globals": ["globals@15.15.0", "https://registry.npmmirror.com/globals/-/globals-15.15.0.tgz", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], + "globe.gl": ["globe.gl@2.46.1", "https://registry.npmmirror.com/globe.gl/-/globe.gl-2.46.1.tgz", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "kapsule": "^1.16", "three": ">=0.179 <1", "three-globe": "^2.45", "three-render-objects": "^1.41" } }, "sha512-h+OvX52EBIPLtM0/2JkM+JZ9gPAhPJ4y3+hxUwD5Ey/O0Zk2ockuTiJ71bZbnNBGmNiIZzA5Vr3TMT0b3d35IQ=="], + "google-auth-library": ["google-auth-library@10.9.0", "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-10.9.0.tgz", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg=="], + "google-logging-utils": ["google-logging-utils@1.1.3", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], "gopd": ["gopd@1.2.0", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], @@ -1822,12 +1893,16 @@ "help-me": ["help-me@5.0.0", "https://registry.npmmirror.com/help-me/-/help-me-5.0.0.tgz", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], + "highlight.js": ["highlight.js@11.11.1", "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], + "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], "html-escaper": ["html-escaper@3.0.3", "https://registry.npmmirror.com/html-escaper/-/html-escaper-3.0.3.tgz", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], "html-void-elements": ["html-void-elements@3.0.0", "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + "htmlparser2": ["htmlparser2@10.1.0", "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-10.1.0.tgz", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], + "http-cache-semantics": ["http-cache-semantics@4.2.0", "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], "http-errors": ["http-errors@2.0.1", "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], @@ -1952,6 +2027,10 @@ "jsonfile": ["jsonfile@4.0.0", "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + "jwa": ["jwa@2.0.1", "https://registry.npmmirror.com/jwa/-/jwa-2.0.1.tgz", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + + "jws": ["jws@4.0.1", "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], + "kapsule": ["kapsule@1.16.3", "https://registry.npmmirror.com/kapsule/-/kapsule-1.16.3.tgz", { "dependencies": { "lodash-es": "4" } }, "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg=="], "keyv": ["keyv@5.6.0", "https://registry.npmmirror.com/keyv/-/keyv-5.6.0.tgz", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], @@ -1960,6 +2039,8 @@ "kleur": ["kleur@4.1.5", "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + "kolorist": ["kolorist@1.8.0", "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], + "libsql": ["libsql@0.5.29", "https://registry.npmmirror.com/libsql/-/libsql-0.5.29.tgz", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.29", "@libsql/darwin-x64": "0.5.29", "@libsql/linux-arm-gnueabihf": "0.5.29", "@libsql/linux-arm-musleabihf": "0.5.29", "@libsql/linux-arm64-gnu": "0.5.29", "@libsql/linux-arm64-musl": "0.5.29", "@libsql/linux-x64-gnu": "0.5.29", "@libsql/linux-x64-musl": "0.5.29", "@libsql/win32-x64-msvc": "0.5.29" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg=="], "lightningcss": ["lightningcss@1.32.0", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], @@ -1992,6 +2073,8 @@ "load-yaml-file": ["load-yaml-file@0.2.0", "https://registry.npmmirror.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.13.0", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw=="], + "local-pkg": ["local-pkg@1.2.1", "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.2.1.tgz", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q=="], + "locate-path": ["locate-path@5.0.0", "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], "lodash": ["lodash@4.18.1", "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], @@ -2014,6 +2097,8 @@ "lowercase-keys": ["lowercase-keys@3.0.0", "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="], + "lowlight": ["lowlight@3.3.0", "https://registry.npmmirror.com/lowlight/-/lowlight-3.3.0.tgz", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "highlight.js": "~11.11.0" } }, "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ=="], + "lru-cache": ["lru-cache@10.4.3", "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "lru.min": ["lru.min@1.1.4", "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.4.tgz", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], @@ -2156,6 +2241,8 @@ "mkdirp-classic": ["mkdirp-classic@0.5.3", "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "mlly": ["mlly@1.8.2", "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="], + "module-details-from-path": ["module-details-from-path@1.0.4", "https://registry.npmmirror.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="], "monaco-editor": ["monaco-editor@0.50.0", "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.50.0.tgz", {}, "sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA=="], @@ -2220,6 +2307,8 @@ "normalize-url": ["normalize-url@8.1.1", "https://registry.npmmirror.com/normalize-url/-/normalize-url-8.1.1.tgz", {}, "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ=="], + "nth-check": ["nth-check@2.1.1", "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "oauth4webapi": ["oauth4webapi@3.8.6", "https://registry.npmmirror.com/oauth4webapi/-/oauth4webapi-3.8.6.tgz", {}, "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ=="], "object-assign": ["object-assign@4.1.1", "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -2260,18 +2349,26 @@ "p-queue": ["p-queue@8.1.1", "https://registry.npmmirror.com/p-queue/-/p-queue-8.1.1.tgz", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + "p-retry": ["p-retry@4.6.2", "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="], + "p-timeout": ["p-timeout@6.1.4", "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.4.tgz", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], "p-try": ["p-try@2.2.0", "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], "package-json-from-dist": ["package-json-from-dist@1.0.1", "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "package-manager-detector": ["package-manager-detector@1.7.0", "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.7.0.tgz", {}, "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ=="], + "pako": ["pako@1.0.11", "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], "parse-latin": ["parse-latin@7.0.0", "https://registry.npmmirror.com/parse-latin/-/parse-latin-7.0.0.tgz", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], "parse5": ["parse5@8.0.1", "https://registry.npmmirror.com/parse5/-/parse5-8.0.1.tgz", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="], + "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], + + "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "https://registry.npmmirror.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], + "path-browserify": ["path-browserify@1.0.1", "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], "path-exists": ["path-exists@4.0.0", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], @@ -2288,6 +2385,8 @@ "pathe": ["pathe@2.0.3", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pend": ["pend@1.2.0", "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], + "pg": ["pg@8.21.0", "https://registry.npmmirror.com/pg/-/pg-8.21.0.tgz", { "dependencies": { "pg-connection-string": "^2.13.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.14.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA=="], "pg-cloudflare": ["pg-cloudflare@1.4.0", "https://registry.npmmirror.com/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="], @@ -2326,6 +2425,8 @@ "pkg-dir": ["pkg-dir@4.2.0", "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], + "pkg-types": ["pkg-types@2.3.1", "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.1.tgz", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], + "playwright": ["playwright@1.60.0", "https://registry.npmmirror.com/playwright/-/playwright-1.60.0.tgz", { "dependencies": { "playwright-core": "1.60.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA=="], "playwright-core": ["playwright-core@1.60.0", "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.60.0.tgz", { "bin": { "playwright-core": "cli.js" } }, "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA=="], @@ -2338,7 +2439,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.15", "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "postcss": ["postcss@8.5.25", "https://registry.npmmirror.com/postcss/-/postcss-8.5.25.tgz", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw=="], "postcss-import": ["postcss-import@15.1.0", "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], @@ -2398,6 +2499,8 @@ "qrcode": ["qrcode@1.5.4", "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="], + "quansync": ["quansync@0.2.11", "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + "queue-microtask": ["queue-microtask@1.2.3", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], @@ -2432,7 +2535,7 @@ "readable-stream": ["readable-stream@4.7.0", "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], - "readdirp": ["readdirp@3.6.0", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "real-require": ["real-require@0.2.0", "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], @@ -2454,6 +2557,8 @@ "rehype": ["rehype@13.0.2", "https://registry.npmmirror.com/rehype/-/rehype-13.0.2.tgz", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="], + "rehype-highlight": ["rehype-highlight@7.0.2", "https://registry.npmmirror.com/rehype-highlight/-/rehype-highlight-7.0.2.tgz", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-text": "^4.0.0", "lowlight": "^3.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA=="], + "rehype-parse": ["rehype-parse@9.0.1", "https://registry.npmmirror.com/rehype-parse/-/rehype-parse-9.0.1.tgz", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], "rehype-raw": ["rehype-raw@7.0.0", "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-7.0.0.tgz", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], @@ -2504,7 +2609,7 @@ "retext-stringify": ["retext-stringify@4.0.0", "https://registry.npmmirror.com/retext-stringify/-/retext-stringify-4.0.0.tgz", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="], - "retry": ["retry@0.12.0", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + "retry": ["retry@0.13.1", "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], "reusify": ["reusify@1.1.0", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], @@ -2526,6 +2631,8 @@ "safer-buffer": ["safer-buffer@2.1.2", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sax": ["sax@1.6.0", "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], + "saxes": ["saxes@6.0.0", "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], "scheduler": ["scheduler@0.23.2", "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], @@ -2648,6 +2755,8 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "svgo": ["svgo@3.3.4", "https://registry.npmmirror.com/svgo/-/svgo-3.3.4.tgz", { "dependencies": { "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0", "sax": "^1.5.0" }, "bin": "./bin/svgo" }, "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg=="], + "symbol-tree": ["symbol-tree@3.2.4", "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], "tailwind-merge": ["tailwind-merge@2.6.1", "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-2.6.1.tgz", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="], @@ -2742,6 +2851,8 @@ "typescript-auto-import-cache": ["typescript-auto-import-cache@0.3.6", "https://registry.npmmirror.com/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="], + "ufo": ["ufo@1.6.4", "https://registry.npmmirror.com/ufo/-/ufo-1.6.4.tgz", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], + "uint8array-extras": ["uint8array-extras@0.3.0", "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-0.3.0.tgz", {}, "sha512-erJsJwQ0tKdwuqI0359U8ijkFmfiTcq25JvvzRVc1VP+2son1NJRXhxcAKJmAW3ajM8JSGAfsAXye8g4s+znxA=="], "ultrahtml": ["ultrahtml@1.6.0", "https://registry.npmmirror.com/ultrahtml/-/ultrahtml-1.6.0.tgz", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="], @@ -2800,7 +2911,7 @@ "victory-vendor": ["victory-vendor@37.3.6", "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-37.3.6.tgz", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], - "vite": ["vite@8.0.16", "https://registry.npmmirror.com/vite/-/vite-8.0.16.tgz", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="], + "vite": ["vite@5.4.21", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], "vitefu": ["vitefu@1.1.3", "https://registry.npmmirror.com/vitefu/-/vitefu-1.1.3.tgz", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], @@ -2852,6 +2963,8 @@ "webidl-conversions": ["webidl-conversions@8.0.1", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="], + "whatwg-encoding": ["whatwg-encoding@3.1.1", "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + "whatwg-mimetype": ["whatwg-mimetype@5.0.0", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], "whatwg-url": ["whatwg-url@16.0.1", "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-16.0.1.tgz", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], @@ -2902,6 +3015,8 @@ "yargs-parser": ["yargs-parser@21.1.1", "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yauzl": ["yauzl@2.10.0", "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + "yocto-queue": ["yocto-queue@1.2.2", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.2.2.tgz", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], "yoctocolors-cjs": ["yoctocolors-cjs@2.1.3", "https://registry.npmmirror.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="], @@ -2918,10 +3033,6 @@ "@asamuzakjp/dom-selector/lru-cache": ["lru-cache@11.5.1", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], - "@astrojs/check/chokidar": ["chokidar@4.0.3", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - - "@astrojs/react/vite": ["vite@5.4.21", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], - "@astrojs/vercel/esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -2944,8 +3055,6 @@ "@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], - "@mapbox/node-pre-gyp/detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "@mapbox/node-pre-gyp/node-fetch": ["node-fetch@2.7.0", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "@opentelemetry/instrumentation-pg/@types/pg": ["@types/pg@8.15.6", "https://registry.npmmirror.com/@types/pg/-/pg-8.15.6.tgz", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="], @@ -2998,8 +3107,6 @@ "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@vitejs/plugin-react/vite": ["vite@5.4.21", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], - "ansi-align/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "ansi-escapes/type-fest": ["type-fest@0.21.3", "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], @@ -3016,8 +3123,6 @@ "astro/tinyexec": ["tinyexec@0.3.2", "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - "astro/vite": ["vite@5.4.21", "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], - "bl/readable-stream": ["readable-stream@3.6.2", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "bullmq/ioredis": ["ioredis@5.10.1", "https://registry.npmmirror.com/ioredis/-/ioredis-5.10.1.tgz", { "dependencies": { "@ioredis/commands": "1.5.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA=="], @@ -3026,9 +3131,13 @@ "cacache/glob": ["glob@10.5.0", "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + "cacheable-request/get-stream": ["get-stream@9.0.1", "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + "cacheable-request/mimic-response": ["mimic-response@4.0.0", "https://registry.npmmirror.com/mimic-response/-/mimic-response-4.0.0.tgz", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], - "chokidar/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "cheerio/parse5": ["parse5@7.3.0", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], "cli-progress/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -3044,14 +3153,20 @@ "cross-spawn/which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "csso/css-tree": ["css-tree@2.2.1", "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], + "cssstyle/lru-cache": ["lru-cache@11.5.1", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], "docker-modem/readable-stream": ["readable-stream@3.6.2", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "dom-serializer/entities": ["entities@4.5.0", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "dot-prop/type-fest": ["type-fest@3.13.1", "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="], "encoding/iconv-lite": ["iconv-lite@0.6.3", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "figlet/commander": ["commander@14.0.3", "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], @@ -3064,12 +3179,14 @@ "gradient-string/chalk": ["chalk@4.1.2", "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "gray-matter/js-yaml": ["js-yaml@3.14.2", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "gray-matter/js-yaml": ["js-yaml@3.15.0", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.15.0.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], "hast-util-from-html/parse5": ["parse5@7.3.0", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], "hast-util-raw/parse5": ["parse5@7.3.0", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "htmlparser2/entities": ["entities@7.0.1", "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "inquirer/ora": ["ora@5.4.1", "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], "inquirer/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -3078,9 +3195,9 @@ "istanbul-reports/html-escaper": ["html-escaper@2.0.2", "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], - "lightningcss/detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "libsql/detect-libc": ["detect-libc@2.0.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], - "load-yaml-file/js-yaml": ["js-yaml@3.14.2", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "load-yaml-file/js-yaml": ["js-yaml@3.15.0", "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.15.0.tgz", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], @@ -3092,9 +3209,9 @@ "minipass-sized/minipass": ["minipass@3.3.6", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], - "node-gyp/env-paths": ["env-paths@2.2.1", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "mlly/pkg-types": ["pkg-types@1.3.1", "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], - "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "node-gyp/env-paths": ["env-paths@2.2.1", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], "nodegit/tar-fs": ["tar-fs@3.1.2", "https://registry.npmmirror.com/tar-fs/-/tar-fs-3.1.2.tgz", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw=="], @@ -3106,13 +3223,17 @@ "p-locate/p-limit": ["p-limit@2.3.0", "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + "parse5-htmlparser2-tree-adapter/parse5": ["parse5@7.3.0", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "parse5-parser-stream/parse5": ["parse5@7.3.0", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + "playwright/fsevents": ["fsevents@2.3.2", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], - "postcss/nanoid": ["nanoid@3.3.12", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + "postcss/nanoid": ["nanoid@3.3.16", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.16.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], "postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], - "prebuild-install/detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "promise-retry/retry": ["retry@0.12.0", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], "prompts/kleur": ["kleur@3.0.3", "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], @@ -3128,8 +3249,6 @@ "send/debug": ["debug@2.6.9", "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "sharp/detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "ssh2/nan": ["nan@2.27.0", "https://registry.npmmirror.com/nan/-/nan-2.27.0.tgz", {}, "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ=="], "stream-browserify/readable-stream": ["readable-stream@3.6.2", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -3140,6 +3259,12 @@ "sucrase/commander": ["commander@4.1.1", "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "svgo/commander": ["commander@7.2.0", "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "svgo/css-tree": ["css-tree@2.3.1", "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="], + + "tailwindcss/chokidar": ["chokidar@3.6.0", "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + "tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], "tar/chownr": ["chownr@3.0.0", "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], @@ -3150,12 +3275,18 @@ "tsx/esbuild": ["esbuild@0.28.0", "https://registry.npmmirror.com/esbuild/-/esbuild-0.28.0.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], + "vite/esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + + "vitest/vite": ["vite@8.0.16", "https://registry.npmmirror.com/vite/-/vite-8.0.16.tgz", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="], + "vscode-css-languageservice/vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], "vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], "vscode-languageserver-protocol/vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -3164,11 +3295,9 @@ "yaml-language-server/request-light": ["request-light@0.5.8", "https://registry.npmmirror.com/request-light/-/request-light-0.5.8.tgz", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], - "yargs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "@astrojs/check/chokidar/readdirp": ["readdirp@4.1.2", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "yaml-language-server/yaml": ["yaml@2.7.1", "https://registry.npmmirror.com/yaml/-/yaml-2.7.1.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], - "@astrojs/react/vite/esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + "yargs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "@astrojs/vercel/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], @@ -3268,8 +3397,6 @@ "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@vitejs/plugin-react/vite/esbuild": ["esbuild@0.21.5", "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "astro/boxen/camelcase": ["camelcase@8.0.0", "https://registry.npmmirror.com/camelcase/-/camelcase-8.0.0.tgz", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], @@ -3330,6 +3457,8 @@ "bullmq/ioredis/@ioredis/commands": ["@ioredis/commands@1.5.1", "https://registry.npmmirror.com/@ioredis/commands/-/commands-1.5.1.tgz", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="], + "cheerio/parse5/entities": ["entities@6.0.1", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "cli-progress/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cli-table3/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -3340,6 +3469,8 @@ "cross-spawn/which/isexe": ["isexe@2.0.0", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + "got/decompress-response/mimic-response": ["mimic-response@4.0.0", "https://registry.npmmirror.com/mimic-response/-/mimic-response-4.0.0.tgz", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], "gradient-string/chalk/ansi-styles": ["ansi-styles@4.3.0", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -3372,12 +3503,18 @@ "minipass-sized/minipass/yallist": ["yallist@4.0.0", "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "mlly/pkg-types/confbox": ["confbox@0.1.8", "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "nodegit/tar-fs/tar-stream": ["tar-stream@3.2.0", "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.2.0.tgz", { "dependencies": { "b4a": "^1.6.4", "bare-fs": "^4.5.5", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg=="], "ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "parse5-htmlparser2-tree-adapter/parse5/entities": ["entities@6.0.1", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "parse5-parser-stream/parse5/entities": ["entities@6.0.1", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "qrcode/yargs/cliui": ["cliui@6.0.0", "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], "qrcode/yargs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -3390,6 +3527,12 @@ "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "svgo/css-tree/mdn-data": ["mdn-data@2.0.30", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], + + "tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.0.tgz", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], @@ -3442,108 +3585,62 @@ "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - "@astrojs/react/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - "@astrojs/react/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - "@astrojs/react/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - "@astrojs/react/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - "@astrojs/react/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - "@astrojs/react/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - "@astrojs/react/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - "@astrojs/react/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - "@astrojs/react/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - "@astrojs/react/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - "@astrojs/react/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - "@astrojs/react/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "@astrojs/react/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "@astrojs/react/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "@astrojs/react/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "@mapbox/node-pre-gyp/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "@mapbox/node-pre-gyp/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "@vitejs/plugin-react/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - - "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "astro/boxen/string-width/emoji-regex": ["emoji-regex@10.6.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "astro/boxen/string-width/strip-ansi": ["strip-ansi@7.2.0", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], diff --git a/cli/src/commands/stack/index.ts b/cli/src/commands/stack/index.ts index 5b0fd937..9b054d82 100644 --- a/cli/src/commands/stack/index.ts +++ b/cli/src/commands/stack/index.ts @@ -270,10 +270,6 @@ stackCommands .action(async () => { try { const roots = await getStackTopology(git); - console.log(chalk.hex("#6272a4")("No stack found.")); - return; - } - const { printSectionHeader, formatBadge } = await import("../../lib/formatter.js"); // Use Dracula colors directly or via chalk diff --git a/deploy/prometheus.yml b/deploy/prometheus.yml index d1c0e246..1d1d1bd9 100644 --- a/deploy/prometheus.yml +++ b/deploy/prometheus.yml @@ -6,6 +6,12 @@ scrape_configs: metrics_path: "/api/metrics" scrape_interval: 15s scrape_timeout: 10s + # /api/metrics is protected by METRICS_TOKEN when set. Provide the token + # via environment expansion, e.g. start prometheus with: + # METRICS_TOKEN= prometheus --config.file=prometheus.yml + # or mount a file containing the token at /etc/prometheus/och-metrics-token + authorization: + credentials_file: /etc/prometheus/och-metrics-token static_configs: - targets: ["opencodehub-app:4321"] labels: diff --git a/doc/docs-parity-baseline.json b/doc/docs-parity-baseline.json index 3e6ef75b..95432a1c 100644 --- a/doc/docs-parity-baseline.json +++ b/doc/docs-parity-baseline.json @@ -1,5 +1,7 @@ { "missingInSite": [ + "cli", + "getting-started/ssh-keys", "support/troubleshooting" ], "missingInDocs": [ diff --git a/docker-compose.nas.yml b/docker-compose.nas.yml index 7683c571..3b67c247 100644 --- a/docker-compose.nas.yml +++ b/docker-compose.nas.yml @@ -1,78 +1,158 @@ +# OpenCodeHub — NAS / home-server stack +# ============================================================ +# Runs everything on a dedicated disk or NAS share by binding every +# data directory to a host path you control (default /mnt/och). +# +# 1. cp .env.example .env +# 2. Set required secrets (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, +# CRON_SECRET, RUNNER_SECRET, AI_CONFIG_ENCRYPTION_KEY, +# WORKFLOW_SECRET_ENCRYPTION_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD) +# 3. (Optional) point OCH_DATA_DIR at your NAS mount or dedicated disk: +# OCH_DATA_DIR=/mnt/nas/opencodehub # or /volume1/opencodehub on Synology +# 4. docker compose -f docker-compose.nas.yml up -d +# +# Storage backends (local + S3 only): +# - local: STORAGE_TYPE=local, everything under ${OCH_DATA_DIR}/storage +# - s3: STORAGE_TYPE=s3 with STORAGE_ENDPOINT/BUCKET/ACCESS_KEY/SECRET +# +# Optional profiles: +# --profile cloudflare Cloudflare Tunnel for external access +# --profile tailscale Tailscale sidecar +# ============================================================ + +x-och-env: &och-env + NODE_ENV: production + DATABASE_DRIVER: postgres + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} + SESSION_SECRET: ${SESSION_SECRET:?Set SESSION_SECRET in .env} + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} + CRON_SECRET: ${CRON_SECRET:?Set CRON_SECRET in .env} + RUNNER_SECRET: ${RUNNER_SECRET:?Set RUNNER_SECRET in .env} + AI_CONFIG_ENCRYPTION_KEY: ${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env} + WORKFLOW_SECRET_ENCRYPTION_KEY: ${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env} + SITE_URL: ${SITE_URL:-http://localhost:4321} + ALLOWED_ORIGINS: ${SITE_URL:-http://localhost:4321} + STORAGE_TYPE: ${STORAGE_TYPE:-local} + STORAGE_PATH: /data/storage + GIT_REPOS_PATH: /data/repos + REPOS_PATH: /data/repos + GIT_SSH_HOST_KEY: /data/ssh/host_key + GIT_SSH_PORT: 2222 + RATE_LIMIT_ENABLED: "true" + RATE_LIMIT_SKIP_DEV: "false" + CSRF_SKIP_DEV: "false" + TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.1,172.16.0.0/12,100.64.0.0/10} + +x-och-common: &och-common + image: opencodehub/opencodehub:latest + restart: unless-stopped + environment: + <<: *och-env + volumes: + - ${OCH_DATA_DIR:-/mnt/och}/repos:/data/repos + - ${OCH_DATA_DIR:-/mnt/och}/storage:/data/storage + - ${OCH_DATA_DIR:-/mnt/och}/ssh:/data/ssh + - ${OCH_DATA_DIR:-/mnt/och}/cache:/data/cache + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - opencodehub-network + services: - # Main OpenCodeHub application + # ── Main application (web + API + Git HTTP) ──────────────── app: - image: swadhinbiswas/opencodehub:latest + <<: *och-common container_name: opencodehub - restart: unless-stopped + environment: + <<: *och-env + PROCESS_TYPE: app + PORT: 4321 ports: - - "4321:4321" # Web interface (Internal) - - "2222:2222" # SSH Git (Internal) + - "${WEB_PORT:-4321}:4321" + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s + + # ── SSH Git server ───────────────────────────────────────── + ssh-git: + <<: *och-common + container_name: opencodehub-ssh environment: - - NODE_ENV=production - - DATABASE_URL=postgresql://opencodehub:opencodehub@postgres:5432/opencodehub - - DATABASE_TYPE=postgres - - REDIS_URL=redis://redis:6379 - - JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required} - - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:?WORKFLOW_SECRET_ENCRYPTION_KEY is required} - - SSH_HOST_KEY_PATH=/data/ssh/host_key - - SERVER_URL=${SITE_URL:-http://localhost:4321} - - STORAGE_TYPE=local - - STORAGE_PATH=/data/storage - - REPOS_PATH=/data/repos - # TRUSTED_PROXIES allows real IP mapping from Cloudflare or Tailscale sidecars - - TRUSTED_PROXIES=127.0.0.1,172.16.0.0/12,100.64.0.0/10 - volumes: - - repos-data:/data/repos - - storage-data:/data/storage - - ssh-data:/data/ssh - - cache-data:/data/cache - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_healthy - networks: - - opencodehub-network + <<: *och-env + PROCESS_TYPE: ssh + ports: + - "${SSH_PORT:-2222}:2222" + + # ── Background worker ────────────────────────────────────── + worker: + <<: *och-common + container_name: opencodehub-worker + environment: + <<: *och-env + PROCESS_TYPE: worker + WORKER_HEALTH_PORT: 9090 + expose: + - "9090" + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/healthz"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s - # PostgreSQL database + # ── PostgreSQL ───────────────────────────────────────────── postgres: - image: postgres:16-alpine + image: docker.io/library/postgres:16-alpine container_name: opencodehub-postgres restart: unless-stopped environment: - POSTGRES_USER: opencodehub - POSTGRES_PASSWORD: opencodehub - POSTGRES_DB: opencodehub + POSTGRES_USER: ${POSTGRES_USER:-opencodehub} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + POSTGRES_DB: ${POSTGRES_DB:-opencodehub} volumes: - - postgres-data:/var/lib/postgresql/data + - ${OCH_DATA_DIR:-/mnt/och}/postgres:/var/lib/postgresql/data networks: - opencodehub-network healthcheck: - test: ["CMD-SHELL", "pg_isready -U opencodehub -d opencodehub"] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-opencodehub} -d ${POSTGRES_DB:-opencodehub}"] interval: 10s timeout: 5s retries: 5 - # Redis for caching and sessions + # ── Redis ────────────────────────────────────────────────── redis: - image: redis:7-alpine + image: docker.io/library/redis:7-alpine container_name: opencodehub-redis restart: unless-stopped - command: redis-server --appendonly yes + command: + - redis-server + - --requirepass + - ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env} + - --appendonly + - "yes" + - --maxmemory + - 256mb + - --maxmemory-policy + - allkeys-lru volumes: - - redis-data:/data + - ${OCH_DATA_DIR:-/mnt/och}/redis:/data networks: - opencodehub-network healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s timeout: 5s retries: 5 - # ------------------------------------------------------------- - # OPTION 1: Cloudflare Tunnel (Uncomment 'profiles' to use) - # Run with: docker-compose -f docker-compose.nas.yml --profile cloudflare up -d - # ------------------------------------------------------------- + # ── Cloudflare Tunnel (optional) ─────────────────────────── cloudflared: image: cloudflare/cloudflared:latest container_name: opencodehub-cloudflared @@ -85,20 +165,18 @@ services: profiles: - cloudflare - # ------------------------------------------------------------- - # OPTION 2: Tailscale (Uncomment 'profiles' to use) - # Run with: docker-compose -f docker-compose.nas.yml --profile tailscale up -d - # ------------------------------------------------------------- + # ── Tailscale (optional) ─────────────────────────────────── tailscale: image: tailscale/tailscale:latest container_name: opencodehub-tailscale hostname: opencodehub-nas + restart: unless-stopped environment: - TS_AUTHKEY=${TS_AUTHKEY:-} - TS_STATE_DIR=/var/lib/tailscale - TS_USERSPACE=false volumes: - - tailscale-data:/var/lib/tailscale + - ${OCH_DATA_DIR:-/mnt/och}/tailscale:/var/lib/tailscale - /dev/net/tun:/dev/net/tun cap_add: - net_admin @@ -108,22 +186,6 @@ services: profiles: - tailscale -volumes: - repos-data: - driver: local - storage-data: - driver: local - ssh-data: - driver: local - cache-data: - driver: local - postgres-data: - driver: local - redis-data: - driver: local - tailscale-data: - driver: local - networks: opencodehub-network: driver: bridge diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 15158cae..fad2fbe3 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -49,10 +49,9 @@ services: restart: unless-stopped expose: - "4321" # Internal only — Caddy proxies to this - ports: - - "${SSH_PORT:-2222}:2222" # SSH Git (exposed directly) environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_DRIVER=postgres - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} - DATABASE_SSL=false @@ -64,7 +63,6 @@ services: - AI_CONFIG_ENCRYPTION_KEY=${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env} - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env} - RUNNER_SECRET=${RUNNER_SECRET:?Set RUNNER_SECRET in .env} - - SSH_HOST_KEY_PATH=/data/ssh/host_key - SITE_URL=${SITE_URL:-https://opencodehub.example.com} - SERVER_URL=${SITE_URL:-https://opencodehub.example.com} - STORAGE_TYPE=${STORAGE_TYPE:-local} @@ -77,9 +75,6 @@ services: - CSRF_SKIP_DEV=false - ALLOWED_ORIGINS=${SITE_URL:-https://opencodehub.example.com} - MAX_REQUEST_BODY_MB=${MAX_REQUEST_BODY_MB:-10} - # SSH server (runs inside app container) - - GIT_SSH_PORT=2222 - - GIT_SSH_HOST_KEY=/data/ssh/host_key volumes: - repos-data:/data/repos - storage-data:/data/storage @@ -121,6 +116,51 @@ services: memory: 512M cpus: "0.5" + # ── SSH Git Server ───────────────────────────────────────── + ssh-git: + build: + context: . + dockerfile: Dockerfile + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "${SSH_PORT:-2222}:2222" # SSH Git (exposed directly) + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_DRIVER=postgres + - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} + - REDIS_URL=redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 + - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env} + - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_PORT=2222 + - GIT_SSH_HOST_KEY=/data/ssh/host_key + volumes: + - repos-data:/data/repos + - storage-data:/data/storage + - ssh-data:/data/ssh + - cache-data:/data/cache + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - opencodehub-internal + - opencodehub-external + logging: + driver: json-file + options: + max-size: "50m" + max-file: "5" + deploy: + resources: + limits: + memory: 512M + cpus: "0.5" + # ── Background Worker ────────────────────────────────────── worker: build: @@ -128,16 +168,22 @@ services: dockerfile: Dockerfile container_name: opencodehub-worker restart: unless-stopped - command: ["bun", "scripts/worker.ts"] expose: - "9090" # Worker health check (internal only) environment: - NODE_ENV=production + - PROCESS_TYPE=worker - DATABASE_DRIVER=postgres - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-opencodehub} - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379 - JWT_SECRET=${JWT_SECRET} + - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET} + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - STORAGE_TYPE=${STORAGE_TYPE:-local} + - STORAGE_PATH=/data/storage - LOG_LEVEL=${LOG_LEVEL:-info} + - WORKER_HEALTH_PORT=9090 volumes: - repos-data:/data/repos - storage-data:/data/storage @@ -304,7 +350,7 @@ services: command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} - MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD in .env} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} volumes: - minio-data:/data expose: diff --git a/docker-compose.yml b/docker-compose.yml index 9558a29f..24d66b93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,108 +1,268 @@ +# OpenCodeHub — self-hosted quickstart stack (production-safe) +# ============================================================ +# 1. cp .env.example .env +# 2. Fill in ALL required secrets in .env +# 3. docker compose up -d +# +# REQUIRED .env values (compose refuses to start without them): +# JWT_SECRET SESSION_SECRET INTERNAL_HOOK_SECRET CRON_SECRET +# RUNNER_SECRET AI_CONFIG_ENCRYPTION_KEY WORKFLOW_SECRET_ENCRYPTION_KEY +# POSTGRES_PASSWORD REDIS_PASSWORD +# → generate each with: openssl rand -hex 32 +# +# Storage — two backends only: +# - local (default): filesystem; point STORAGE_PATH/GIT_REPOS_PATH at a +# dedicated disk or NAS mount for best performance +# - s3: any S3-compatible store (AWS S3, MinIO, R2, Garage, ...) via +# STORAGE_ENDPOINT/STORAGE_BUCKET/STORAGE_ACCESS_KEY_ID/STORAGE_SECRET_ACCESS_KEY +# +# Optional profiles: +# docker compose --profile with-runner up -d # CI/CD runner (needs Docker-in-Docker) +# docker compose --profile with-minio up -d # local S3-compatible storage +# ============================================================ + services: - # Main OpenCodeHub application + # ── Main OpenCodeHub application (web + API + Git HTTP) ──── app: build: context: . dockerfile: Dockerfile - container_name: opencodehub + container_name: opencodehub-app restart: unless-stopped + env_file: .env + environment: + NODE_ENV: production + PORT: 4321 + PROCESS_TYPE: app + DATABASE_DRIVER: postgres + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} + SESSION_SECRET: ${SESSION_SECRET:?Set SESSION_SECRET in .env} + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} + CRON_SECRET: ${CRON_SECRET:?Set CRON_SECRET in .env} + RUNNER_SECRET: ${RUNNER_SECRET:?Set RUNNER_SECRET in .env} + AI_CONFIG_ENCRYPTION_KEY: ${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env} + WORKFLOW_SECRET_ENCRYPTION_KEY: ${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env} + SITE_URL: ${SITE_URL:-http://localhost:4321} + ALLOWED_ORIGINS: ${SITE_URL:-http://localhost:4321} + GIT_REPOS_PATH: /data/repos + REPOS_PATH: /data/repos + GIT_SSH_PORT: 2222 + GIT_SSH_HOST_KEY: /data/ssh/host_key + STORAGE_TYPE: ${STORAGE_TYPE:-local} + STORAGE_PATH: /data/storage + RATE_LIMIT_ENABLED: "true" + RATE_LIMIT_SKIP_DEV: "false" + CSRF_SKIP_DEV: "false" ports: - - "4321:4321" # Web interface - - "2222:2222" # SSH Git + - "${WEB_PORT:-4321}:4321" # Web UI + API + Git HTTP + volumes: + - repos-data:/data/repos + - storage-data:/data/storage + - ssh-data:/data/ssh + - cache-data:/data/cache + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - och + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s + + # ── SSH Git server ───────────────────────────────────────── + ssh-git: + build: + context: . + dockerfile: Dockerfile + container_name: opencodehub-ssh + restart: unless-stopped environment: - - NODE_ENV=production - - DATABASE_URL=postgresql://opencodehub:opencodehub@postgres:5432/opencodehub - - DATABASE_TYPE=postgres - - REDIS_URL=redis://redis:6379 - - JWT_SECRET=${JWT_SECRET:-supersecretjwtkey} - - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:-0123456789abcdef0123456789abcdef} - - SERVER_URL=http://localhost:4321 - - DATA_DIR=/data - - STORAGE_TYPE=local + NODE_ENV: production + PROCESS_TYPE: ssh + DATABASE_DRIVER: postgres + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} + GIT_REPOS_PATH: /data/repos + REPOS_PATH: /data/repos + GIT_SSH_PORT: 2222 + GIT_SSH_HOST_KEY: /data/ssh/host_key + ports: + - "${SSH_PORT:-2222}:2222" # SSH Git volumes: - - ./data/opencodehub:/data + - repos-data:/data/repos + - storage-data:/data/storage + - ssh-data:/data/ssh + - cache-data:/data/cache depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - - opencodehub-network + - och + + # ── Background worker (merge queue, webhooks, mirrors, digests) ── + worker: + build: + context: . + dockerfile: Dockerfile + container_name: opencodehub-worker + restart: unless-stopped + environment: + NODE_ENV: production + PROCESS_TYPE: worker + DATABASE_DRIVER: postgres + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} + GIT_REPOS_PATH: /data/repos + REPOS_PATH: /data/repos + STORAGE_TYPE: ${STORAGE_TYPE:-local} + STORAGE_PATH: /data/storage + WORKER_HEALTH_PORT: 9090 + expose: + - "9090" # Worker healthz/ready + volumes: + - repos-data:/data/repos + - storage-data:/data/storage + - ssh-data:/data/ssh + - cache-data:/data/cache + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - och healthcheck: - test: - [ - "CMD", - "wget", - "--no-verbose", - "--tries=1", - "--spider", - "http://localhost:4321/api/health", - ] + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/healthz"] interval: 30s timeout: 10s retries: 3 - start_period: 10s + start_period: 20s - # PostgreSQL database + # ── PostgreSQL ───────────────────────────────────────────── postgres: image: docker.io/library/postgres:16-alpine container_name: opencodehub-postgres restart: unless-stopped environment: - POSTGRES_USER: opencodehub - POSTGRES_PASSWORD: opencodehub - POSTGRES_DB: opencodehub + POSTGRES_USER: ${POSTGRES_USER:-opencodehub} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} + POSTGRES_DB: ${POSTGRES_DB:-opencodehub} volumes: - - ./data/postgres:/var/lib/postgresql/data - ports: - - "5432:5432" + - postgres-data:/var/lib/postgresql/data networks: - - opencodehub-network + - och healthcheck: - test: ["CMD-SHELL", "pg_isready -U opencodehub -d opencodehub"] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-opencodehub} -d ${POSTGRES_DB:-opencodehub}"] interval: 10s timeout: 5s retries: 5 - # Redis for caching and sessions + # ── Redis (sessions, caching, distributed locking, queues) ── redis: image: docker.io/library/redis:7-alpine container_name: opencodehub-redis restart: unless-stopped - command: redis-server --appendonly yes + command: + - redis-server + - --requirepass + - ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env} + - --appendonly + - "yes" + - --maxmemory + - 256mb + - --maxmemory-policy + - allkeys-lru volumes: - - ./data/redis:/data - ports: - - "6379:6379" + - redis-data:/data networks: - - opencodehub-network + - och healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s timeout: 5s retries: 5 - # Pipeline runner (for CI/CD jobs) + # ── CI/CD Runner (Docker-in-Docker, optional) ────────────── runner: build: context: . dockerfile: Dockerfile.runner container_name: opencodehub-runner restart: unless-stopped - privileged: true # Required for Docker-in-Docker + privileged: true # Required for Docker-in-Docker environment: - - RUNNER_TOKEN=${RUNNER_TOKEN:-} - - SERVER_URL=http://app:4321 - - DOCKER_HOST=unix:///var/run/docker.sock + RUNNER_TOKEN: ${RUNNER_TOKEN:-} + SERVER_URL: http://app:4321 + DOCKER_HOST: unix:///var/run/docker.sock + RUNNER_WORK_DIR: /work + RUNNER_CACHE_DIR: /cache volumes: - /var/run/docker.sock:/var/run/docker.sock - - ./data/runner/work:/work - - ./data/runner/cache:/cache + - runner-work:/work + - runner-cache:/cache depends_on: - app networks: - - opencodehub-network + - och + profiles: + - with-runner + + # ── MinIO (S3-compatible storage, optional) ──────────────── + minio: + image: minio/minio:latest + container_name: opencodehub-minio + restart: unless-stopped + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} + ports: + - "${MINIO_CONSOLE_PORT:-9001}:9001" # Web console (set 0 to disable) + volumes: + - minio-data:/data + networks: + - och + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 30s + timeout: 10s + retries: 3 + profiles: + - with-minio + +volumes: + repos-data: + driver: local + storage-data: + driver: local + ssh-data: + driver: local + cache-data: + driver: local + postgres-data: + driver: local + redis-data: + driver: local + runner-work: + driver: local + runner-cache: + driver: local + minio-data: + driver: local networks: - opencodehub-network: + och: driver: bridge diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 37799e8d..ceacb6d0 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -5,6 +5,17 @@ echo "╔═══════════════════════ echo "║ OpenCodeHub Starting... ║" echo "╚══════════════════════════════════════════╝" +# ── Process type ────────────────────────────────────────────── +# app (default) — web UI + API + Git HTTP +# ssh — SSH git server (scripts/ssh-server.ts) +# worker — background jobs (scripts/worker.ts) +PROCESS_TYPE="${PROCESS_TYPE:-app}" + +# ── Create required directories ────────────────────────────── +mkdir -p "${GIT_REPOS_PATH:-/data/repos}" "${STORAGE_PATH:-/data/storage}" "${CACHE_PATH:-/data/cache}" +mkdir -p "$(dirname "${LOG_FILE:-/data/logs/opencodehub.log}")" +mkdir -p "${SSH_PATH:-/data/ssh}" + # ── Wait for database ──────────────────────────────────────── if [ -n "$DATABASE_URL" ]; then echo "⏳ Waiting for database..." @@ -23,23 +34,39 @@ if [ -n "$DATABASE_URL" ]; then sleep 1 done - # Run database migrations + # Run database migrations (deterministic — applies committed migrations in drizzle/) echo "🔄 Running database migrations..." cd /app - bunx drizzle-kit push --force 2>/dev/null || echo "⚠️ Migration push skipped (may already be up to date)" + if [ -d "/app/drizzle" ]; then + bun scripts/migrate.ts + else + echo "⚠️ No drizzle/ migrations found — skipping (use 'npm run db:push' in dev)" + fi echo "✅ Migrations complete" fi # ── Initialize SSH host key ────────────────────────────────── -if [ -n "$SSH_HOST_KEY_PATH" ] && [ ! -f "$SSH_HOST_KEY_PATH" ]; then +HOST_KEY="${GIT_SSH_HOST_KEY:-${SSH_HOST_KEY_PATH:-/data/ssh/host_key}}" +if [ -n "$HOST_KEY" ] && [ ! -f "$HOST_KEY" ]; then echo "🔑 Generating SSH host key..." - ssh-keygen -t ed25519 -f "$SSH_HOST_KEY_PATH" -N "" -q + mkdir -p "$(dirname "$HOST_KEY")" + ssh-keygen -t ed25519 -f "$HOST_KEY" -N "" -q echo "✅ SSH host key generated" fi -# ── Create required directories ────────────────────────────── -mkdir -p "${REPOS_PATH:-/data/repos}" "${STORAGE_PATH:-/data/storage}" "${CACHE_PATH:-/data/cache}" -mkdir -p "$(dirname "${LOG_FILE:-/data/logs/opencodehub.log}")" - -echo "🚀 Starting OpenCodeHub on port ${PORT:-4321}..." -exec bun ./dist/server/entry.mjs +# ── Start the requested process ────────────────────────────── +cd /app +case "$PROCESS_TYPE" in + ssh) + echo "🚀 Starting OpenCodeHub SSH Git server on port ${GIT_SSH_PORT:-2222}..." + exec bun scripts/ssh-server.ts + ;; + worker) + echo "🚀 Starting OpenCodeHub background worker..." + exec bun scripts/worker.ts + ;; + *) + echo "🚀 Starting OpenCodeHub on port ${PORT:-4321}..." + exec bun ./dist/server/entry.mjs + ;; +esac diff --git a/docs-site/README.md b/docs-site/README.md index 12fffe89..8ee7ea9c 100644 --- a/docs-site/README.md +++ b/docs-site/README.md @@ -1,49 +1,56 @@ -# Starlight Starter Kit: Basics +# OpenCodeHub Documentation Site -[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) +The official documentation for [OpenCodeHub](https://github.com/swadhinbiswas/OpencodeHub) — an open-source, self-hosted Git platform with Stacked PRs, AI Code Review, and Smart Merge Queues. Built with [Astro Starlight](https://starlight.astro.build) and deployed to [Cloudflare Workers](https://workers.cloudflare.com). -``` -bun create astro@latest -- --template starlight -``` +**Live site:** -> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! +## Project Structure -## 🚀 Project Structure -Inside of your Astro + Starlight project, you'll see the following folders and files: -``` -. -├── public/ -├── src/ -│ ├── assets/ -│ ├── content/ -│ │ └── docs/ -│ └── content.config.ts -├── astro.config.mjs -├── package.json -└── tsconfig.json -``` +Starlight looks for `.md` or `.mdx` files in `src/content/docs/`. Each file is exposed as a route based on its file name. + +Images go in `src/assets/` and are embedded in Markdown with relative links. Static assets like favicons go in `public/`. + +## Commands -Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. +All commands run from the project root: -Images can be added to `src/assets/` and embedded in Markdown with a relative link. +| Command | Action | +| :-------------------- | :------------------------------------------------ | +| `bun install` | Install dependencies | +| `bun dev` | Start local dev server at `localhost:4321` | +| `bun build` | Build production site to `./dist/` | +| `bun preview` | Preview the production build locally | +| `bun deploy` | Deploy to Cloudflare Workers via Wrangler | +| `bun astro ...` | Run CLI commands like `astro add`, `astro check` | +| `bun astro -- --help` | Get help using the Astro CLI | -Static assets, like favicons, can be placed in the `public/` directory. +## Development -## 🧞 Commands +```bash +# Install dependencies +bun install -All commands are run from the root of the project, from a terminal: +# Start dev server +bun dev +``` + +The dev server runs at `http://localhost:4321`. Edit files in `src/content/docs/` and the site hot-reloads. + +## Deployment + +This site is deployed to Cloudflare Workers. To deploy: + +```bash +bun build +bun deploy +``` -| Command | Action | -| :------------------------ | :----------------------------------------------- | -| `bun install` | Installs dependencies | -| `bun dev` | Starts local dev server at `localhost:4321` | -| `bun build` | Build your production site to `./dist/` | -| `bun preview` | Preview your build locally, before deploying | -| `bun astro ...` | Run CLI commands like `astro add`, `astro check` | -| `bun astro -- --help` | Get help using the Astro CLI | +The `wrangler.jsonc` configures the worker to serve the static `dist/` directory. See [Cloudflare deployment guide](https://docs.opencodehub.space/administration/deploy-cloudflare/) for details. -## 👀 Want to learn more? +## Tech Stack -Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). +- **Framework:** [Astro](https://astro.build) with [Starlight](https://starlight.astro.build) documentation theme +- **Deployment:** [Cloudflare Workers](https://workers.cloudflare.com) (static assets) +- **Package Manager:** [Bun](https://bun.sh) diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index 9c554247..173f56ab 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -4,6 +4,7 @@ import { defineConfig } from "astro/config"; // https://astro.build/config export default defineConfig({ + site: "https://docs.opencodehub.space", integrations: [ starlight({ title: "OpenCodeHub Docs", @@ -42,6 +43,7 @@ export default defineConfig({ { label: "Branch Protection", slug: "guides/branch-protection" }, { label: "Webhooks", slug: "guides/webhooks" }, { label: "Storage Adapters", slug: "guides/storage-adapters" }, + { label: "CircleCI Integration", slug: "guides/circleci-integration" }, ], }, { @@ -65,11 +67,13 @@ export default defineConfig({ { label: "Stacked PRs", slug: "features/stacked-prs" }, { label: "AI Review", slug: "features/ai-review" }, { label: "Merge Queue", slug: "features/merge-queue" }, + { label: "CI/CD Actions", slug: "features/ci-actions" }, + { label: "CI/CD Pipelines", slug: "features/ci-cd" }, { label: "Automation Rules", slug: "features/automations" }, { label: "PR Inbox", slug: "features/inbox" }, { label: "Developer Metrics", slug: "features/developer-metrics" }, { label: "Notifications", slug: "features/notifications" }, - { label: "CI/CD Actions", slug: "features/ci-actions" }, + { label: "Slack Integration", slug: "features/slack-integration" }, { label: "CLI Workflows", slug: "features/cli" }, ], }, @@ -86,6 +90,9 @@ export default defineConfig({ { label: "cPanel", slug: "administration/deploy-cpanel" }, { label: "CyberPanel", slug: "administration/deploy-cyberpanel" }, { label: "Cloudflare", slug: "administration/deploy-cloudflare" }, + { label: "Coolify", slug: "administration/deploy-coolify" }, + { label: "NAS (Synology/TrueNAS)", slug: "administration/deploy-nas" }, + { label: "Kubernetes (Helm)", slug: "administration/kubernetes" }, ], }, { @@ -95,6 +102,19 @@ export default defineConfig({ { label: "Configuration", slug: "administration/configuration" }, { label: "Monitoring", slug: "administration/monitoring" }, { label: "Security", slug: "administration/security" }, + { label: "Deployment Matrix", slug: "administration/deployment-matrix" }, + { label: "Secret Rotation", slug: "administration/secret-rotation" }, + { label: "Expose NAS / Home Server", slug: "administration/expose-nas" }, + { label: "CircleCI Cloud Workers", slug: "administration/circleci-cloud" }, + { label: "CircleCI Self-Hosted", slug: "administration/circleci-worker" }, + ], + }, + { + label: "Operations", + items: [ + { label: "Incident Runbook", slug: "administration/incident-runbook" }, + { label: "Postmortem Template", slug: "administration/postmortem-template" }, + { label: "Operations Drills", slug: "administration/operations-drills" }, ], }, { @@ -103,11 +123,24 @@ export default defineConfig({ }, { label: "Development", - autogenerate: { directory: "development" }, + items: [ + { label: "Architecture", slug: "development/architecture" }, + { label: "Local Dev Setup", slug: "development/local-dev-setup" }, + { label: "Contributing", slug: "development/contributing" }, + { label: "Database Schema", slug: "development/database-schema" }, + { label: "Database Migrations", slug: "development/database-migrations" }, + { label: "Testing", slug: "development/testing" }, + { label: "Testing & CI", slug: "development/testing-pipelines" }, + { label: "Security Hardening", slug: "development/security-hardening" }, + { label: "Observability", slug: "development/observability" }, + { label: "Publishing CLI", slug: "development/publishing-cli" }, + ], }, { label: "Reference", - autogenerate: { directory: "reference" }, + items: [ + { label: "Glossary", slug: "reference/glossary" }, + ], }, ], }), diff --git a/docs-site/src/content/docs/FREE-DEPLOYMENT.md b/docs-site/src/content/docs/FREE-DEPLOYMENT.md new file mode 100644 index 00000000..a4bc2234 --- /dev/null +++ b/docs-site/src/content/docs/FREE-DEPLOYMENT.md @@ -0,0 +1,399 @@ +--- +title: "Free Deployment Options" +--- + +# OpenCodeHub — Free Deployment Options + +> Deploy OpenCodeHub for free with persistent file storage for git repositories. + +--- + +## Quick Comparison + +| Platform | Cost | Storage | RAM | CPU | Best For | +|----------|------|---------|-----|-----|----------| +| **Oracle Cloud Free** | $0 forever | 200GB | 24GB | 4 ARM cores | Best overall | +| **Home Server/NAS** | $0 | Unlimited | Your hardware | Your hardware | Privacy-focused | +| **Fly.io** | $0 (limited) | 3GB free | 256MB | Shared | Quick testing | +| **Google Cloud Free** | $0 forever | 30GB | 1GB | 0.25 vCPU | Light usage | +| **Railway** | $5 trial | 1GB | 512MB | Shared | Quick demo | +| **Hetzner** | ~€4/mo | 40GB | 4GB | 2 vCPU | Best value paid | + +--- + +## Option 1: Oracle Cloud Free Tier (Recommended) + +**Always Free** — No credit card charge, no time limit. + +### What You Get +- 4 ARM cores (Ampere A1) +- 24 GB RAM +- 200 GB boot volume +- 2 x 1TB block volumes (total 2TB) +- Free PostgreSQL database +- Free Redis + +### Steps + +```bash +# 1. Sign up at cloud.oracle.com +# 2. Create a VM instance: +# - Name: opencodehub +# - Image: Ubuntu 22.04 +# - Shape: VM.Standard.A1.Flex (4 OCPU, 24 GB RAM) +# - Upload SSH key +# - Create boot volume: 100 GB + +# 3. SSH into your instance +ssh -i ~/.ssh/key ubuntu@YOUR_PUBLIC_IP + +# 4. Install Docker +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +sudo usermod -aG docker $USER + +# 5. Install Docker Compose +sudo apt install docker-compose-plugin -y + +# 6. Clone OpenCodeHub +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub + +# 7. Configure +cp .env.example .env +nano .env # Edit secrets + +# 8. Start +sudo docker compose up -d + +# 9. Create admin +sudo docker compose exec app bun run scripts/seed-admin.ts +``` + +### Access Your Instance +``` +Web: http://YOUR_PUBLIC_IP:4321 +SSH: ssh://git@YOUR_PUBLIC_IP:2222/owner/repo.git +HTTPS: Set up Nginx + Let's Encrypt (free) +``` + +--- + +## Option 2: Home Server / NAS + +**$0 forever** — Use hardware you already own. + +### What You Need +- Any computer running Linux/Mac/Windows +- 2GB+ RAM recommended +- 20GB+ free disk space + +### Quick Start (Linux/Mac) + +```bash +# Install Docker +curl -fsSL https://get.docker.com | sh + +# Clone and start +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env +nano .env # Set secrets +docker compose up -d + +# Create admin +docker compose exec app bun run scripts/seed-admin.ts +``` + +### NAS Deployment + +**Synology / TrueNAS / QNAP:** +1. Install Docker from Package Center +2. Use Docker Compose or Portainer +3. Mount a shared folder for `/data` + +### Access from Anywhere +- **Tailscale** (free): `tailscale up` → access via Tailscale IP +- **Cloudflare Tunnel** (free): Expose without opening ports +- **Port forwarding**: Open port 4321 on your router + +--- + +## Option 3: Fly.io Free Tier + +**Free** — 3GB persistent volume included. + +### Steps + +```bash +# 1. Install flyctl +curl -L https://fly.io/install.sh | sh + +# 2. Login +fly auth login + +# 3. Launch +fly launch --name opencodehub + +# 4. Add persistent volume +fly volume create opencodehub_data --region sjc --size 3 + +# 5. Set secrets +fly secrets set JWT_SECRET=$(openssl rand -hex 32) +fly secrets set SESSION_SECRET=$(openssl rand -hex 32) +fly secrets set DATABASE_URL="postgresql://..." +fly secrets set REDIS_URL="redis://..." + +# 6. Deploy +fly deploy +``` + +### Limitations +- 3GB storage (enough for small repos) +- 256MB RAM (may be slow for large repos) +- Apps休眠 after inactivity (cold starts take ~30s) + +--- + +## Option 4: Google Cloud Free Tier + +**Always Free** — e2-micro instance. + +### Steps + +```bash +# 1. Sign up at cloud.google.com +# 2. Create e2-micro VM: +# - Machine type: e2-micro (0.25 vCPU, 1GB RAM) +# - Boot disk: 30GB SSD (free) +# - Firewall: Allow HTTP (80), SSH (22) + +# 3. SSH in and install Docker +ssh -i ~/.ssh/key USER@EXTERNAL_IP +sudo apt update && sudo apt install docker.io docker-compose-plugin -y + +# 4. Deploy OpenCodeHub +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env +nano .env +docker compose up -d +``` + +### Limitations +- Only 1GB RAM (tight for production) +- 0.25 vCPU (slow builds) +- Good for personal/light use + +--- + +## Option 5: Self-Hosted on Existing VPS + +If you already have a VPS (Hetzner, DigitalOcean, Linode, etc.): + +```bash +# SSH into your server +ssh root@YOUR_SERVER_IP + +# One-line install +curl -fsSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash +``` + +--- + +## Option 6: Docker on Windows/Mac + +For local development or personal use: + +```bash +# Install Docker Desktop for Windows/Mac +# https://docs.docker.com/get-docker/ + +# Clone and start +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env +nano .env +docker compose up -d + +# Create admin +docker compose exec app bun run scripts/seed-admin.ts +``` + +--- + +## Storage Configuration + +### Local Storage (Default) +```bash +STORAGE_TYPE=local +STORAGE_PATH=/data/storage +GIT_REPOS_PATH=/data/repositories +``` + +### S3-Compatible (MinIO on same server) +```bash +# Add to docker-compose.yml: +minio: + image: minio/minio + command: server /data --console-address ":9001" + volumes: + - ./data/minio:/data + ports: + - "9001:9001" + +STORAGE_TYPE=s3 +STORAGE_ENDPOINT=http://minio:9000 +STORAGE_BUCKET=opencodehub +STORAGE_ACCESS_KEY_ID=minioadmin +STORAGE_SECRET_ACCESS_KEY=minioadmin +``` + +--- + +## HTTPS Setup (Free) + +### With Let's Encrypt + Nginx + +```bash +# Install Nginx and Certbot +sudo apt install nginx certbot python3-certbot-nginx -y + +# Create Nginx config +sudo nano /etc/nginx/sites-available/opencodehub +``` + +```nginx +server { + listen 80; + server_name git.yourdomain.com; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + server_name git.yourdomain.com; + + ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; + + client_max_body_size 500M; + + location / { + proxy_pass http://localhost:4321; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # WebSocket support + location /api/realtime { + proxy_pass http://localhost:4321; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +```bash +# Enable and get SSL +sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/ +sudo certbot --nginx -d git.yourdomain.com +``` + +--- + +## Environment Variables (Production) + +```bash +# Generate all secrets +JWT_SECRET=$(openssl rand -hex 32) +SESSION_SECRET=$(openssl rand -hex 32) +INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) +CRON_SECRET=$(openssl rand -hex 32) +RUNNER_SECRET=$(openssl rand -hex 32) +AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) +WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) + +# Your .env file +DATABASE_DRIVER=postgres +DATABASE_URL=postgresql://opencodehub:PASSWORD@localhost:5432/opencodehub +DATABASE_SSL=false +REDIS_URL=redis://localhost:6379 +SITE_URL=https://git.yourdomain.com +STORAGE_TYPE=local +GIT_REPOS_PATH=/data/repositories +NODE_ENV=production +``` + +--- + +## Backup Strategy + +### Automated Backup Script +```bash +#!/bin/bash +# backup.sh — Run daily via cron +BACKUP_DIR="/backups/$(date +%Y-%m-%d)" +mkdir -p "$BACKUP_DIR" + +# Database dump +docker compose exec -T postgres pg_dump -U opencodehub opencodehub > "$BACKUP_DIR/db.sql" + +# Git repos +tar czf "$BACKUP_DIR/repos.tar.gz" ./data/repositories/ + +# Upload to S3 (optional) +# aws s3 sync "$BACKUP_DIR" s3://your-backup-bucket/opencodehub/ +``` + +### Cron Job +```bash +# Add to crontab +crontab -e +0 2 * * * /path/to/backup.sh +``` + +--- + +## Monitoring + +### Health Check +```bash +# Check if app is running +curl -f http://localhost:4321/api/health || echo "DOWN" + +# Check Docker status +docker compose ps +docker compose logs --tail=50 app +``` + +### Uptime Robot (Free) +1. Sign up at uptimerobot.com +2. Add HTTP monitor: `http://YOUR_IP:4321/api/health` +3. Get notified if your instance goes down + +--- + +## Recommended Free Setup + +For a **personal Git server** that stores all your code: + +| Component | Choice | +|-----------|--------| +| **Cloud** | Oracle Cloud Free Tier | +| **Server** | ARM A1 (4 cores, 24GB RAM) | +| **Storage** | 100GB boot + 1TB block volume | +| **Database** | PostgreSQL (Docker) | +| **Cache** | Redis (Docker) | +| **HTTPS** | Nginx + Let's Encrypt | +| **Access** | Tailscale (private) or port forwarding | +| **Backups** | Cron script + local S3 | + +**Total cost: $0/month forever.** + +--- + +*Generated for OpenCodeHub free deployment.* diff --git a/docs-site/src/content/docs/RENDER-DEPLOYMENT.md b/docs-site/src/content/docs/RENDER-DEPLOYMENT.md new file mode 100644 index 00000000..7ba616c9 --- /dev/null +++ b/docs-site/src/content/docs/RENDER-DEPLOYMENT.md @@ -0,0 +1,167 @@ +--- +title: "Render Deployment Guide" +--- + +# OpenCodeHub — Render Deployment Guide + +> Deploy to Render (Singapore) with free PostgreSQL + Upstash Redis. $0/month. + +--- + +## What You Get + +- OpenCodeHub running in Asia (Singapore) +- Free PostgreSQL database +- Free Redis via Upstash +- 1GB persistent storage for git repos +- HTTPS included +- Admin account + +--- + +## Step 1: Create Upstash Redis (2 min) + +1. Go to **https://upstash.com** → Sign up (free) +2. Click **Create Database** +3. Settings: + - **Name**: `opencodehub` + - **Region**: **Asia Pacific (Singapore)** + - **Plan**: Pay as you go (free tier: 10K commands/day) +4. Click **Create** +5. Copy the **Redis URL** (looks like `rediss://...@...`) +6. Keep this tab open — you'll need it in Step 3 + +--- + +## Step 2: Deploy to Render (5 min) + +1. Go to **https://render.com** → Sign up (free) +2. Click **New** → **Blueprint** +3. Connect your GitHub account +4. Select your OpenCodeHub repository +5. Render detects `render.yaml` and shows: + - `opencodehub-db` (PostgreSQL — Singapore) + - `opencodehub` (Web Service — Singapore) +6. Click **Apply** +7. Wait for deployment (~3-5 min first time) + +--- + +## Step 3: Set Redis URL (1 min) + +1. Go to your Web Service → **Environment** tab +2. Find `REDIS_URL` (shows "Not set") +3. Click the pencil icon → Paste your **Upstash Redis URL** +4. Click **Save** + +--- + +## Step 4: Set Site URL (1 min) + +1. In the same **Environment** tab +2. Find `SITE_URL` (shows "Not set") +3. Click the pencil icon → Enter your Render URL: + ``` + https://opencodehub.onrender.com + ``` + (Replace with your actual URL from the service overview) +4. Click **Save** + +--- + +## Step 5: Manual Deploy + +1. Go to your Web Service → **Manual Deploy** +2. Click **Deploy latest commit** +3. Wait for build to complete (~2-3 min) +4. Click the **URL** at the top to open your instance + +--- + +## Step 6: Create Admin Account + +You'll see the **Setup Wizard**. Fill in: + +| Field | Value | +|-------|-------| +| Username | `admin` (or your choice) | +| Email | Your email | +| Password | Strong password | + +Click **Create Account** — you're now logged in as admin! + +--- + +## Step 7: Test It + +```bash +# Create a repo via web UI, then clone: +git clone https://opencodehub.onrender.com/your-username/my-repo.git + +# Push code: +cd my-repo +echo "# Hello" > README.md +git add . && git commit -m "Initial" +git push origin main +``` + +--- + +## Done! Summary + +| Component | Service | Region | Cost | +|-----------|---------|--------|------| +| App | Render Free | Singapore | $0 | +| PostgreSQL | Render Free | Singapore | $0 | +| Redis | Upstash Free | Singapore | $0 | +| Storage | Render Disk 1GB | Singapore | $0 | +| **Total** | | | **$0/month** | + +--- + +## Keep Alive (Optional) + +Render free tier spins down after 15min inactivity. To prevent: + +1. Go to **https://uptimerobot.com** → Sign up (free) +2. Add Monitor → Type: HTTP(s) +3. URL: `https://opencodehub.onrender.com/api/health` +4. Monitoring interval: 5 minutes +5. Save — your app stays awake + +--- + +## Upgrade Path (When You Grow) + +| Need | Upgrade | Cost | +|------|---------|------| +| Always-on (no spin-down) | Render Starter | $7/mo | +| More storage (10GB+) | Render Disk upgrade | $1-7/mo | +| More Redis commands | Upstash Pay-as-you-go | ~$0.20/10K | +| PostgreSQL expiry | Render Starter DB | $7/mo | + +--- + +## Troubleshooting + +**App won't start?** +- Check **Logs** tab in Render +- Verify `REDIS_URL` is set correctly +- Ensure `SITE_URL` matches your Render URL + +**Redis connection failed?** +- Verify Redis URL from Upstash (starts with `rediss://`) +- Check Upstash dashboard shows "Active" +- Ensure region is Singapore + +**Git clone fails?** +- Use HTTPS: `git clone https://your-url.onrender.com/user/repo.git` +- SSH not available on Render free tier (use HTTPS) + +**Slow first request?** +- Normal for free tier (cold start ~30s) +- Add UptimeRobot ping to keep it warm + +--- + +*OpenCodeHub — Deployed on Render, Asia Region, $0/month.* diff --git a/docs-site/src/content/docs/administration/air-gapped.md b/docs-site/src/content/docs/administration/air-gapped.md new file mode 100644 index 00000000..0ad4dc59 --- /dev/null +++ b/docs-site/src/content/docs/administration/air-gapped.md @@ -0,0 +1,33 @@ +# Air-gapped Environments + +OpenCodeHub can be deployed in fully offline, air-gapped networks. + +## Prerequisites +- A local container registry to host the OpenCodeHub Docker image. +- A local NPM/mirror registry if you plan to build from source. + +## Deployment Steps +1. On a machine with internet access, pull the required images: + ```bash + docker pull ghcr.io/swadhinbiswas/opencodehub:latest + docker pull postgres:15-alpine + docker pull redis:7-alpine + ``` +2. Save the images to tar archives: + ```bash + docker save ghcr.io/swadhinbiswas/opencodehub:latest -o och.tar + docker save postgres:15-alpine -o pg.tar + docker save redis:7-alpine -o redis.tar + ``` +3. Transfer the archives to the air-gapped network via secure media. +4. Load the images on the target machine: + ```bash + docker load -i och.tar + docker load -i pg.tar + docker load -i redis.tar + ``` + +## Configuration Adjustments +- Disable external OAuth providers (GitHub, Google) and rely exclusively on local authentication, LDAP, or an internal SAML provider. +- Disable outbound webhooks pointing to external SaaS platforms. +- Ensure the AI Code Review module is configured to use a `local` provider (e.g., an internal Ollama instance) rather than cloud APIs like OpenAI. diff --git a/docs-site/src/content/docs/administration/backup-restore.md b/docs-site/src/content/docs/administration/backup-restore.md new file mode 100644 index 00000000..2522445b --- /dev/null +++ b/docs-site/src/content/docs/administration/backup-restore.md @@ -0,0 +1,29 @@ +# Backup and Restore + +This guide covers how to back up and restore an OpenCodeHub instance. + +## Backup Strategy + +### 1. Database +For PostgreSQL, use `pg_dump`: +```bash +pg_dump -U opencodehub -d opencodehub_db > opencodehub_backup.sql +``` + +### 2. Git Repositories +The Git repositories are stored in the path defined by `GIT_REPOS_PATH` (default: `./data/repos`). +You can back this up using `rsync` or `tar`: +```bash +tar -czvf repos_backup.tar.gz ./data/repos/ +``` + +### 3. Object Storage +If you are using S3 or another object storage provider for artifacts, ensure you have enabled versioning and standard bucket backups through your cloud provider. If using local storage, back up the configured `./data/storage` directory. + +## Restore Strategy +To restore: +1. Stop the application and worker processes. +2. Restore the database using `psql`. +3. Extract the Git repositories to `GIT_REPOS_PATH`. +4. Restore object storage data. +5. Restart the application. diff --git a/docs-site/src/content/docs/administration/configuration.md b/docs-site/src/content/docs/administration/configuration.md index 6f3a53ad..9edd85b7 100644 --- a/docs-site/src/content/docs/administration/configuration.md +++ b/docs-site/src/content/docs/administration/configuration.md @@ -2,64 +2,215 @@ title: "Configuration Reference" --- +OpenCodeHub is configured via environment variables. Copy `.env.example` to `.env` and edit. -OpenCodeHub is configured exclusively via environment variables. +## Server -## Core +| Variable | Description | Default | +|----------|-------------|---------| +| `HOST` | Bind address | `0.0.0.0` | +| `PORT` | HTTP port | `4321` | +| `SITE_URL` | Public URL of the instance | `http://localhost:4321` | +| `APP_NAME` | Instance name | `OpenCodeHub` | +| `NODE_ENV` | `development` or `production` | `development` | -| Variable | Description | Required | Default | -|----------|-------------|----------|---------| -| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:3000` | -| `PORT` | Port to listen on | No | `3000` | -| `NODE_ENV` | `development` or `production` | No | `development` | +## Database -## Security (Critical) +| Variable | Description | Default | +|----------|-------------|---------| +| `DATABASE_DRIVER` | `postgres`, `sqlite`, or `turso` (LibSQL) | `postgres` | +| `DATABASE_URL` | Connection string | — | +| `DATABASE_POOL_SIZE` | Connection pool size | `10` | +| `DATABASE_SSL` | Enable PostgreSQL SSL | `false` | +| `DATABASE_AUTH_TOKEN` | Auth token (Turso/LibSQL only) | — | + +:::caution[Production] +PostgreSQL is **required** for production. All schemas use `pgTable`. SQLite is for development only. +::: + +## Redis + +| Variable | Description | Default | +|----------|-------------|---------| +| `REDIS_URL` | Redis connection string | `redis://localhost:6379` | +| `REDIS_PASSWORD` | Redis password | — | + +Redis is required in production for distributed locking, rate limiting, and sessions. For edge deployments (Vercel), use Upstash Redis with `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`. + +## Authentication | Variable | Description | Generate With | |----------|-------------|---------------| -| `JWT_SECRET` | Signs auth tokens | `openssl rand -hex 32` | -| `SESSION_SECRET` | Signs session cookies | `openssl rand -hex 32` | -| `INTERNAL_HOOK_SECRET` | Secures git hooks | `openssl rand -hex 32` | +| `JWT_SECRET` | Signs auth tokens | `openssl rand -base64 32` | +| `JWT_EXPIRES_IN` | Token expiry | `7d` | +| `SESSION_SECRET` | Signs session cookies | `openssl rand -base64 32` | -## Database +## Security Secrets -| Variable | Description | Example | +All secrets below must be unique. Generate each with `openssl rand -hex 32`. + +| Variable | Purpose | +|----------|---------| +| `INTERNAL_HOOK_SECRET` | Secures git hook communication between Git server and app | +| `CRON_SECRET` | Protects cron endpoints (GC, mirror sync, notification digests) | +| `AI_CONFIG_ENCRYPTION_KEY` | Encrypts AI provider API keys at rest | +| `WORKFLOW_SECRET_ENCRYPTION_KEY` | Encrypts CI/CD workflow secrets at rest | +| `RUNNER_SECRET` | Shared secret between server and CI runners | +| `METRICS_TOKEN` | Bearer token for `GET /api/metrics` (Prometheus). Empty = public | + +## OAuth Providers + +| Variable | Description | +|----------|-------------| +| `OAUTH_GOOGLE_CLIENT_ID` | Google OAuth client ID | +| `OAUTH_GOOGLE_CLIENT_SECRET` | Google OAuth client secret | +| `OAUTH_GITHUB_CLIENT_ID` | GitHub OAuth client ID | +| `OAUTH_GITHUB_CLIENT_SECRET` | GitHub OAuth client secret | +| `OAUTH_GITLAB_CLIENT_ID` | GitLab OAuth client ID | +| `OAUTH_GITLAB_CLIENT_SECRET` | GitLab OAuth client secret | + +OpenCodeHub also supports SSO via SAML and OIDC. Configure these through the admin dashboard. + +## Git Configuration + +| Variable | Description | Default | |----------|-------------|---------| -| `DATABASE_DRIVER` | `postgres`, `mysql`, `sqlite`, `libsql` | `postgres` | -| `DATABASE_URL` | Connection string | `postgresql://user:pass@host:5432/db` | -| `DATABASE_AUTH_TOKEN`| Auth token (Turso/LibSQL only) | | +| `GIT_REPOS_PATH` | Directory for git repositories | `./data/repositories` | +| `GIT_SSH_PORT` | SSH git server port | `2222` | +| `GIT_SSH_HOST_KEY` | Path to SSH host key | `./data/ssh/host_key` | ## Storage | Variable | Description | Default | |----------|-------------|---------| -| `STORAGE_TYPE` | `local` (filesystem) or `s3` (any S3-compatible object store) | `local` | -| `STORAGE_PATH` | Path for local storage | `./data/storage` | -| `STORAGE_BUCKET` | Bucket name (required for `s3`) | | -| `STORAGE_REGION` | S3 region | `us-east-1` | -| `STORAGE_ENDPOINT` | Custom S3 endpoint (MinIO, R2, Garage, SeaweedFS, Ceph RGW, etc.) | | -| `STORAGE_ACCESS_KEY_ID` | S3 access key | | -| `STORAGE_SECRET_ACCESS_KEY` | S3 secret key | | +| `STORAGE_TYPE` | `local` or `s3` | `local` | +| `STORAGE_PATH` | Local storage path | `./data/storage` | -> Only `local` and `s3` storage backends are supported. Previous releases -> also supported `gdrive`, `azure`, `gcs`, `dropbox`, `onedrive`, `ftp`, and -> an rclone-as-adapter option; these have been removed in favour of -> S3-compatible object storage. See `docs/guides/storage-adapters.md`. +### S3-Compatible Storage -## AI Review +Works with AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, Ceph RGW, Wasabi, Backblaze B2, and any S3-compatible service. | Variable | Description | |----------|-------------| -| `AI_PROVIDER` | `openai` or `anthropic` | -| `OPENAI_API_KEY` | OpenAI API Key | -| `ANTHROPIC_API_KEY` | Anthropic API Key | +| `STORAGE_BUCKET` | Bucket name (required for S3) | +| `STORAGE_REGION` | S3 region (default: `us-east-1`) | +| `STORAGE_ENDPOINT` | Custom S3 endpoint (for MinIO, R2, etc.) | +| `STORAGE_ACCESS_KEY_ID` | S3 access key | +| `STORAGE_SECRET_ACCESS_KEY` | S3 secret key | + +:::tip[Performance] +For best performance, use **local SSD storage**. Object storage (S3/R2) introduces latency. Use S3 for multi-node deployments or backup. +::: + +## CI/CD Runner + +| Variable | Description | Default | +|----------|-------------|---------| +| `RUNNER_ENABLED` | Enable the CI runner | `true` | +| `RUNNER_DOCKER_SOCKET` | Docker socket path | `/var/run/docker.sock` | +| `RUNNER_WORKSPACE_PATH` | Workspace directory | `./data/runner/workspaces` | +| `RUNNER_ARTIFACTS_PATH` | Artifacts directory | `./data/runner/artifacts` | +| `RUNNER_CONCURRENT_JOBS` | Max concurrent jobs | `2` | +| `RUNNER_TIMEOUT` | Job timeout in seconds | `3600` | +| `RUNNER_REGISTRATION_TOKEN_TTL_DAYS` | Token TTL | `90` | ## Email (SMTP) +| Variable | Description | Default | +|----------|-------------|---------| +| `SMTP_HOST` | SMTP hostname | — | +| `SMTP_PORT` | SMTP port | `587` | +| `SMTP_USER` | SMTP username | — | +| `SMTP_PASSWORD` | SMTP password | — | +| `SMTP_FROM` | From address | `noreply@opencodehub.local` | + +## Logging + +| Variable | Description | Default | +|----------|-------------|---------| +| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` | `info` | +| `LOG_FILE` | Log file path | `./data/logs/opencodehub.log` | + +## Rate Limiting + +| Variable | Description | Default | +|----------|-------------|---------| +| `RATE_LIMIT_ENABLED` | Enable rate limiting | `true` | +| `RATE_LIMIT_AUTH` | Auth endpoints (req/15min) | `5` | +| `RATE_LIMIT_API` | API endpoints (req/min) | `100` | +| `RATE_LIMIT_GIT` | Git operations (req/min) | `200` | +| `RATE_LIMIT_GENERAL` | General endpoints (req/min) | `60` | +| `RATE_LIMIT_SKIP_DEV` | Skip in development | `false` | +| `CSRF_SKIP_DEV` | Skip CSRF in development | `false` | + +## CORS & Request Limits + +| Variable | Description | Default | +|----------|-------------|---------| +| `ALLOWED_ORIGINS` | Comma-separated allowed origins | `http://localhost:4321` | +| `MAX_REQUEST_BODY_MB` | Max request body size (MB) | `10` | +| `MAX_PACK_SIZE_MB` | Max git pack size (MB) | `500` | +| `GIT_PROCESS_TIMEOUT_SECS` | Git process timeout (sec) | `300` | + +## Feature Flags + +| Variable | Description | Default | +|----------|-------------|---------| +| `ENABLE_REGISTRATION` | Allow new user signups | `true` | +| `ENABLE_WIKI` | Enable repository wikis | `true` | +| `ENABLE_ISSUES` | Enable issue tracking | `true` | +| `ENABLE_ACTIONS` | Enable CI/CD pipelines | `true` | +| `ENABLE_PACKAGES` | Enable package registry | `false` | +| `ENABLE_AI_ASSISTANT` | Enable AI code review | `true` | + +## AI Providers + +Configure one or more AI providers for code review. Keys are encrypted at rest using `AI_CONFIG_ENCRYPTION_KEY`. + +| Variable | Provider | +|----------|----------| +| `OPENAI_API_KEY` | OpenAI (GPT-4, GPT-4o) | +| `ANTHROPIC_API_KEY` | Anthropic (Claude) | +| `GROQ_API_KEY` | Groq (fast inference) | +| `BYTEZ_API_KEY` | Bytez | +| `OPENROUTER_API_KEY` | OpenRouter (multi-model) | +| `TOGETHER_API_KEY` | Together AI | +| `GOOGLE_AI_API_KEY` | Google AI (Gemini) | + +Ollama is supported for local/self-hosted AI. No API key required — just set the Ollama endpoint in the admin dashboard. + +## External Review Agent + +| Variable | Description | +|----------|-------------| +| `EXTERNAL_AGENT_WEBHOOK_URL` | Webhook URL for external AI agent | +| `EXTERNAL_AGENT_API_KEY` | API key for the external agent | +| `EXTERNAL_AGENT_CALLBACK_SECRET` | Callback verification secret | + +## Backup + +| Variable | Description | Default | +|----------|-------------|---------| +| `BACKUP_ENCRYPTION_KEY` | Encryption key for backups | — | +| `BACKUP_S3_BUCKET` | S3 bucket for backups | — | +| `BACKUP_S3_PREFIX` | S3 prefix | `opencodehub-backups` | +| `BACKUP_RETENTION_DAYS` | Retention period | `30` | +| `BACKUP_DIR` | Local backup directory | `./backups` | + +## Air-gapped Mode + +| Variable | Description | Default | +|----------|-------------|---------| +| `AIR_GAPPED_MODE` | Disable all external API calls | `false` | + +When enabled, OpenCodeHub operates fully offline. AI features are disabled unless you run a local model (e.g., Ollama). + +## Observability + | Variable | Description | |----------|-------------| -| `SMTP_HOST` | Hostname (e.g. `smtp.gmail.com`) | -| `SMTP_PORT` | Port (e.g. `587`) | -| `SMTP_USER` | Username | -| `SMTP_PASSWORD`| Password | -| `SMTP_FROM` | From address | +| `GRAFANA_OTLP_ENDPOINT` | Grafana Cloud OTLP endpoint | +| `GRAFANA_INSTANCE_ID` | Grafana Cloud instance ID | +| `GRAFANA_API_KEY` | Grafana Cloud API key | + +OpenCodeHub exports OpenTelemetry traces and Prometheus metrics. See [Monitoring](/administration/monitoring/) for setup details. diff --git a/docs-site/src/content/docs/administration/deploy-nas.md b/docs-site/src/content/docs/administration/deploy-nas.md index c22da001..81e1ba84 100644 --- a/docs-site/src/content/docs/administration/deploy-nas.md +++ b/docs-site/src/content/docs/administration/deploy-nas.md @@ -3,23 +3,42 @@ title: "Deploy on NAS (Synology, TrueNAS, QNAP)" description: "Deploy OpenCodeHub on your Network Attached Storage device using Docker" --- +# Deploy OpenCodeHub on NAS (Synology, TrueNAS, QNAP) + > **Difficulty:** Intermediate | **Time:** 30-45 minutes | **Method:** Docker/Container Station Deploy OpenCodeHub on your **Network Attached Storage (NAS)** device. This is perfect for small teams, personal use, or as a private Git server at home. Works on Synology DSM, TrueNAS SCALE, QNAP QTS, and any NAS supporting Docker. +> **Recommended:** the repository ships `docker-compose.nas.yml`, which is +> the production-safe version of every snippet below. It runs the web app, +> the **SSH Git server**, and the **background worker** as separate +> containers, requires secrets instead of defaulting them, and binds every +> data directory to one host path (`OCH_DATA_DIR`, default `/mnt/och`) — +> perfect for a dedicated hard disk or NAS share: +> +> ```bash +> cp .env.example .env # then fill in ALL secrets +> OCH_DATA_DIR=/volume1/opencodehub docker compose -f docker-compose.nas.yml up -d +> ``` +> +> The inline YAML in this guide is a minimal reference. Note that the **web +> app container does not run SSH Git** — if you use a snippet as-is, port +> 2222 will not serve SSH. Add the `ssh-git` service (or use +> `docker-compose.nas.yml`) for SSH access. + --- ## Prerequisites | NAS Type | Minimum Requirements | |----------|---------------------| -| **Synology** | DSM 7.0+, 4GB RAM (8GB recommended), x86_64 or ARM64 | +| **Synology** | DSM 7.0+, 4GB RAM (8GB recommended), x86_64 or ARM64 (better performance on x86_64) | | **TrueNAS SCALE** | 8GB RAM, 2 CPU cores, 40GB free space | | **QNAP** | QTS 5.0+, 4GB RAM, Container Station installed | | **Generic** | Docker/Podman support, 4GB RAM, 2 CPU cores | -- A domain with DDNS configured (e.g., `git.yourddns.net`) OR local network access only -- Port forwarding on your router (if accessing externally): +- A domain with DDNS (Dynamic DNS) configured (e.g., `git.yourddns.net`) OR local network access only +- Port forwarding configured on your router (if accessing externally): - `80` → NAS:80 - `443` → NAS:443 - `2222` → NAS:2222 (for SSH Git, optional) @@ -138,7 +157,6 @@ docker run -d \ --link opencodehub-postgres:postgres \ --link opencodehub-redis:redis \ -p 4321:4321 \ - -p 2222:2222 \ -e NODE_ENV=production \ -e DATABASE_URL=postgresql://opencodehub:POSTGRES_PASS@host.docker.internal:5432/opencodehub \ -e REDIS_URL=redis://:REDIS_PASS@host.docker.internal:6379 \ @@ -148,19 +166,24 @@ docker run -d \ -e CRON_SECRET=$(openssl rand -hex 32) \ -e RUNNER_SECRET=$(openssl rand -hex 32) \ -e WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) \ + -e AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) \ -e SITE_URL=https://git.yourddns.net \ -e STORAGE_TYPE=local \ -e STORAGE_PATH=/data/storage \ - -e REPOS_PATH=/data/repos \ - -e SSH_HOST_KEY_PATH=/data/ssh/host_key \ + -e GIT_REPOS_PATH=/data/repos \ + -e GIT_SSH_HOST_KEY=/data/ssh/host_key \ -v /volume1/opencodehub/repos:/data/repos \ -v /volume1/opencodehub/storage:/data/storage \ -v /volume1/opencodehub/ssh:/data/ssh \ -v /volume1/opencodehub/cache:/data/cache \ - swadhinbiswas/opencodehub:latest + opencodehub/opencodehub:latest ``` > **Note:** On Synology, `host.docker.internal` may not work. Use your NAS IP address instead (e.g., `192.168.1.100`). +> +> **SSH Git:** the app container serves the web UI only. For SSH access, +> run a second container from the same image with `PROCESS_TYPE=ssh` and +> expose port 2222 (this is exactly what `docker-compose.nas.yml` does). #### Option B: Build from Source @@ -178,9 +201,9 @@ services: restart: unless-stopped ports: - "4321:4321" - - "2222:2222" environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub - REDIS_URL=redis://:YOUR_PASS@redis:6379 - JWT_SECRET=YOUR_SECRET @@ -189,11 +212,72 @@ services: - CRON_SECRET=YOUR_SECRET - RUNNER_SECRET=YOUR_SECRET - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET - SITE_URL=https://git.yourddns.net - STORAGE_TYPE=local - STORAGE_PATH=/data/storage + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /volume1/opencodehub/repos:/data/repos + - /volume1/opencodehub/storage:/data/storage + - /volume1/opencodehub/ssh:/data/ssh + - /volume1/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + ssh-git: + build: + context: /volume1/docker/OpenCodeHub + dockerfile: Dockerfile + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "2222:2222" + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /volume1/opencodehub/repos:/data/repos + - /volume1/opencodehub/storage:/data/storage + - /volume1/opencodehub/ssh:/data/ssh + - /volume1/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + worker: + build: + context: /volume1/docker/OpenCodeHub + dockerfile: Dockerfile + container_name: opencodehub-worker + restart: unless-stopped + environment: + - NODE_ENV=production + - PROCESS_TYPE=worker + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos - REPOS_PATH=/data/repos - - SSH_HOST_KEY_PATH=/data/ssh/host_key + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage volumes: - /volume1/opencodehub/repos:/data/repos - /volume1/opencodehub/storage:/data/storage @@ -304,14 +388,14 @@ version: '3.8' services: app: - image: swadhinbiswas/opencodehub:latest + image: opencodehub/opencodehub:latest container_name: opencodehub restart: unless-stopped ports: - "4321:4321" - - "2222:2222" environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub - REDIS_URL=redis://:YOUR_PASS@redis:6379 - JWT_SECRET=YOUR_SECRET @@ -320,11 +404,68 @@ services: - CRON_SECRET=YOUR_SECRET - RUNNER_SECRET=YOUR_SECRET - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET - SITE_URL=https://git.yourdomain.com - STORAGE_TYPE=local - STORAGE_PATH=/data/storage + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /mnt/YOURPOOL/opencodehub/repos:/data/repos + - /mnt/YOURPOOL/opencodehub/storage:/data/storage + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh + - /mnt/YOURPOOL/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + ssh-git: + image: opencodehub/opencodehub:latest + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "2222:2222" + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /mnt/YOURPOOL/opencodehub/repos:/data/repos + - /mnt/YOURPOOL/opencodehub/storage:/data/storage + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh + - /mnt/YOURPOOL/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + worker: + image: opencodehub/opencodehub:latest + container_name: opencodehub-worker + restart: unless-stopped + environment: + - NODE_ENV=production + - PROCESS_TYPE=worker + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos - REPOS_PATH=/data/repos - - SSH_HOST_KEY_PATH=/data/ssh/host_key + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage volumes: - /mnt/YOURPOOL/opencodehub/repos:/data/repos - /mnt/YOURPOOL/opencodehub/storage:/data/storage @@ -446,7 +587,7 @@ Install Nginx Proxy Manager from the TrueNAS Apps catalog: #### Create OpenCodeHub: -1. Search and install `swadhinbiswas/opencodehub:latest` +1. Search and install `opencodehub/opencodehub:latest` 2. Name: `opencodehub` 3. Network: `opencodehub-network` 4. Environment variables (same as Synology example) @@ -485,20 +626,13 @@ Install Nginx Proxy Manager from the TrueNAS Apps catalog: ## Part 4: Generic NAS / Any Docker Host -If your NAS supports Docker (via Portainer, Podman, or CLI) and you have SSH access, the absolute easiest way to deploy is using the 1-click install script: +If your NAS supports Docker (via Portainer, Podman, or CLI): -```bash -# Connect to your NAS via SSH, then run: -curl -sSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash -``` - -### Using Docker Compose (Manual Setup) - -If you prefer to deploy manually via Portainer or `docker-compose.yml`: +### Using Docker Compose: ```bash -# Create unified data directory -mkdir -p ~/opencodehub-data +# Create directories +mkdir -p ~/opencodehub/{repos,storage,ssh,cache,postgres,redis} # Create docker-compose.yml cat > ~/opencodehub/docker-compose.yml << 'EOF' @@ -506,14 +640,14 @@ version: '3.8' services: app: - image: swadhinbiswas/opencodehub:latest + image: opencodehub/opencodehub:latest container_name: opencodehub restart: unless-stopped ports: - "4321:4321" - - "2222:2222" environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub - REDIS_URL=redis://:YOUR_PASS@redis:6379 - JWT_SECRET=YOUR_SECRET @@ -522,10 +656,73 @@ services: - CRON_SECRET=YOUR_SECRET - RUNNER_SECRET=YOUR_SECRET - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET - SITE_URL=https://git.yourdomain.com - - DATA_DIR=/data + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - ./repos:/data/repos + - ./storage:/data/storage + - ./ssh:/data/ssh + - ./cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + ssh-git: + image: opencodehub/opencodehub:latest + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "2222:2222" + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - ./repos:/data/repos + - ./storage:/data/storage + - ./ssh:/data/ssh + - ./cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + worker: + image: opencodehub/opencodehub:latest + container_name: opencodehub-worker + restart: unless-stopped + environment: + - NODE_ENV=production + - PROCESS_TYPE=worker + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage volumes: - - ~/opencodehub-data:/data + - ./repos:/data/repos + - ./storage:/data/storage + - ./ssh:/data/ssh + - ./cache:/data/cache depends_on: - postgres - redis @@ -541,7 +738,7 @@ services: - POSTGRES_PASSWORD=YOUR_PASS - POSTGRES_DB=opencodehub volumes: - - ~/opencodehub-data/postgres:/var/lib/postgresql/data + - ./postgres:/var/lib/postgresql/data networks: - och-net @@ -551,7 +748,7 @@ services: restart: unless-stopped command: redis-server --appendonly yes --requirepass YOUR_PASS volumes: - - ~/opencodehub-data/redis:/data + - ./redis:/data networks: - och-net @@ -715,14 +912,14 @@ OpenCodeHub on NAS typically uses: ### QNAP - Use **QuTS hero** (ZFS) for better data integrity -- Enable **snapshots** for the opencodehub dataset +- Enable ** snapshots** for the opencodehub dataset - **QVPN** can secure remote access without exposing ports --- ## Next Steps -- [Configure Email (SMTP)](/administration/configuration/#email) -- [Set up Offsite Storage (S3/R2)](/guides/storage-adapters/) — highly recommended for NAS -- [Enable Branch Protection](/guides/branch-protection/) -- [Set up your First Stack](/tutorials/your-first-stack/) +- [Configure Email (SMTP)](../administration/configuration.md#email) +- [Set up Offsite Storage (S3/R2)](../guides/storage-adapters.md) — highly recommended for NAS (protects against drive failure) +- [Enable Branch Protection](../guides/branch-protection.md) +- [Set up your First Stack](../tutorials/your-first-stack.md) diff --git a/docs-site/src/content/docs/administration/deployment.md b/docs-site/src/content/docs/administration/deployment.md index e8ad8738..57ba10b3 100644 --- a/docs-site/src/content/docs/administration/deployment.md +++ b/docs-site/src/content/docs/administration/deployment.md @@ -105,19 +105,9 @@ STORAGE_SECRET_ACCESS_KEY= > removed; use the `s3` driver with a vendor-specific `STORAGE_ENDPOINT` > for object storage. See `docs/guides/storage-adapters.md` for the > full list of compatible object stores. -```bash -STORAGE_TYPE=gdrive -GOOGLE_CLIENT_ID=... -GOOGLE_CLIENT_SECRET=... -GOOGLE_REFRESH_TOKEN=... -GOOGLE_FOLDER_ID=... -``` **Optional but Recommended:** ```bash -# Error Monitoring -SENTRY_DSN=https://your-sentry-dsn - # Email SMTP_HOST=smtp.example.com SMTP_PORT=587 @@ -126,7 +116,7 @@ SMTP_PASSWORD= SMTP_FROM=noreply@yourcompany.com # Redis (for session management) -REDIS_URL=redis://localhost:6379 +REDIS_URL=redis://:your-password@localhost:6379 ``` ### Deployment Steps @@ -147,11 +137,9 @@ nano .env # Configure all production values bun run src/lib/env-validation.ts # Should output: ✅ Environment validation passed -# 5. Run database migrations -npm run db:push -# or generate versioned migrations: -npm run db:generate -npm run db:migrate +# 5. Run database migrations (applies committed drizzle/ migrations) +bun run migrate +# or for dev: npm run db:push # 6. Create admin user bun run scripts/seed-admin.ts @@ -160,59 +148,68 @@ bun run scripts/seed-admin.ts # 7. Build for production npm run build -# 8. Start production server -npm start +# 8. Start the production server (built in step 7) +npm start # → node ./dist/server/entry.mjs ``` +> **Docker** is the recommended path for production. Use the repository's +> `docker-compose.yml` (quickstart) or `docker-compose.production.yml` +> (Caddy TLS + isolated networks) — do not hand-roll a compose file. + ### Docker Deployment (Recommended) -```yaml -# docker-compose.prod.yml -version: '3.8' +Use the repository's own compose files — they are kept in sync with the +codebase and are production-safe (no default secrets, migrations applied +at boot, worker + SSH services included): + +```bash +# Quickstart (app + SSH + worker + PostgreSQL + Redis) +cp .env.example .env # then set ALL secrets +docker compose up -d + +# Production profile (adds Caddy TLS termination + isolated networks) +docker compose -f docker-compose.production.yml --profile production up -d + +# NAS / dedicated-disk deployments +OCH_DATA_DIR=/mnt/nas/opencodehub docker compose -f docker-compose.nas.yml up -d +``` +Minimal reference compose (for illustration only): + +```yaml services: app: build: . restart: always ports: - - "3000:3000" - env_file: .env.production + - "4321:4321" + env_file: .env + environment: + NODE_ENV: production depends_on: - postgres - redis - volumes: - - ./data:/app/data - environment: - NODE_ENV: production - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] - interval: 30s - timeout: 10s - retries: 3 postgres: - image: postgres:15-alpine + image: postgres:16-alpine restart: always environment: POSTGRES_DB: opencodehub POSTGRES_USER: opencodehub POSTGRES_PASSWORD: ${DATABASE_PASSWORD} - volumes: - - postgres_data:/var/lib/postgresql/data - ports: - - "5432:5432" redis: image: redis:7-alpine restart: always command: redis-server --requirepass ${REDIS_PASSWORD} - ports: - - "6379:6379" volumes: postgres_data: ``` +> Exposed ports: **4321** (web/API/Git HTTP) and **2222** (SSH Git). +> PostgreSQL (5432) and Redis (6379) stay on an internal network. + ```bash # Deploy with Docker docker-compose -f docker-compose.prod.yml up -d @@ -243,7 +240,7 @@ server { # Proxy configuration location / { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -257,7 +254,7 @@ server { # Git operations (larger timeouts) location ~ ^/[^/]+/[^/]+\.git/ { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_read_timeout 600s; proxy_send_timeout 600s; proxy_buffer_size 128k; diff --git a/docs-site/src/content/docs/administration/expose-nas.md b/docs-site/src/content/docs/administration/expose-nas.md index 96733b67..6eb68d8b 100644 --- a/docs-site/src/content/docs/administration/expose-nas.md +++ b/docs-site/src/content/docs/administration/expose-nas.md @@ -42,7 +42,7 @@ firewall never sees an inbound connection to OpenCodeHub ports. Whatever exposure method you pick, you need: -1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `3000`). +1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `4321`). 2. **A reachable hostname** (Tailscale or Cloudflare) pointed at the tunnel. 3. **A persistent `cloudflared` or `tailscaled`** process — running on the NAS, in Docker, or as a system service. 4. **Trusted Proxies** correctly set in OpenCodeHub (so the real client IP is preserved and rate limits are not bypassed). See `TRUSTED_PROXIES` in `.env.example`. @@ -104,11 +104,11 @@ tailscale ip -4 # 100.x.y.z # in the Tailscale admin console: https://login.tailscale.com/admin # (turn on the "Funnel" feature flag). # -# Then on the NAS, expose the port OpenCodeHub listens on (default 3000): -sudo tailscale funnel 3000 on +# Then on the NAS, expose the port OpenCodeHub listens on (default 4321): +sudo tailscale funnel 4321 on # Or expose a specific path with HTTPS-only: -sudo tailscale funnel --https=443 http://localhost:3000 +sudo tailscale funnel --https=443 http://localhost:4321 ``` You will get a public URL like `https://nas-hostname.tail-net.ts.net`. @@ -136,7 +136,7 @@ For Synology DSM, use **Task Scheduler → Triggered task → Boot-up** and run: ```bash -sudo tailscale funnel 3000 on +sudo tailscale funnel 4321 on ``` For TrueNAS SCALE, configure the Tailscale app's "Post-Start" command @@ -266,11 +266,11 @@ In the Cloudflare dashboard → your tunnel → **Public Hostname**: |---|---| | Subdomain | `git` (or anything) | | Domain | `example.com` | -| **Service** | Type: `HTTP`, URL: `opencodehub:3000` (or `localhost:3000` if `cloudflared` is host-networking) | +| **Service** | Type: `HTTP`, URL: `opencodehub:4321` (or `localhost:4321` if `cloudflared` is host-networking) | If you used Option A (Docker bridge network), `opencodehub` must be the service name in your OpenCodeHub `docker-compose.yml`. If you used -host networking, use `localhost:3000`. +host networking, use `localhost:4321`. Save. After a few seconds, `https://git.example.com` should reach your OpenCodeHub instance. @@ -382,8 +382,8 @@ on the NAS itself. - Cloudflare: the tunnel status is **HEALTHY** in the Zero Trust dashboard. - Confirm OpenCodeHub is reachable from the tunnel's network namespace: - - Tailscale (host networking): `curl http://localhost:3000` - - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:3000/` + - Tailscale (host networking): `curl http://localhost:4321` + - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:4321/` ### Real client IP is wrong diff --git a/docs-site/src/content/docs/administration/horizontal-scaling.md b/docs-site/src/content/docs/administration/horizontal-scaling.md new file mode 100644 index 00000000..8df9dbe4 --- /dev/null +++ b/docs-site/src/content/docs/administration/horizontal-scaling.md @@ -0,0 +1,17 @@ +# Horizontal Scaling + +OpenCodeHub is designed to be scaled horizontally to handle high traffic and large workloads. + +## Stateless Application Servers +The main Astro application and REST API endpoints are mostly stateless. You can run multiple instances of the app behind a load balancer (e.g., Nginx, HAProxy, or a cloud LB). + +## State Management +- **Database**: Ensure your database (PostgreSQL) is appropriately scaled or managed (e.g., AWS RDS). +- **Redis**: Redis is strictly required for horizontal scaling. It handles sessions, rate limiting, distributed locking, and BullMQ queues for the background workers. +- **Background Workers**: You can spin up multiple worker processes (`npm run worker:start`) across different nodes. They will pull jobs from the shared Redis queue. + +## Shared Git Storage +To allow multiple app instances to access Git repositories over SSH and HTTP, the `GIT_REPOS_PATH` must point to a shared network file system (e.g., NFS, EFS) that supports POSIX file locking. + +## Pluggable Blob Storage +Configure `STORAGE_TYPE=s3` and use an S3-compatible backend (MinIO, AWS S3, Cloudflare R2) so all nodes share access to the same artifact/LFS storage. diff --git a/docs-site/src/content/docs/administration/monitoring.md b/docs-site/src/content/docs/administration/monitoring.md index d353f133..8abb1c12 100644 --- a/docs-site/src/content/docs/administration/monitoring.md +++ b/docs-site/src/content/docs/administration/monitoring.md @@ -12,6 +12,8 @@ The minimum production observability stack should include: - `GET /api/health` for liveness/readiness - `GET /api/metrics` for Prometheus scraping + (protected by `METRICS_TOKEN` when set — pass it as a Bearer token in the + scrape config; see `deploy/prometheus.yml`) - Grafana dashboards for request latency, queue health, and storage/DB timing - Alerting for p95 regressions and service degradation diff --git a/docs-site/src/content/docs/development/architecture.md b/docs-site/src/content/docs/development/architecture.md index 780efae5..872ea7a1 100644 --- a/docs-site/src/content/docs/development/architecture.md +++ b/docs-site/src/content/docs/development/architecture.md @@ -3,57 +3,108 @@ title: "System Architecture" description: "High-level overview of OpenCodeHub's components and data flow." --- -import { Tabs, TabItem } from '@astrojs/starlight/components'; - -OpenCodeHub is built for performance and scalability, using a modern stack optimized for self-hosting. - -## C4 Container Diagram - -This diagram visualizes the high-level interaction between the user, the CLI, and the server infrastructure. - -C4Container - title Container diagram for OpenCodeHub System - - Person(developer, "Developer", "Uses CLI and Web UI to manage code") - - System_Boundary(c1, "OpenCodeHub Platform") { - Container(cli, "CLI Tool", "TypeScript, Node.js", "Handles stacking, pushing, and rapid stack manipulation") - Container(web_app, "Web Application", "Astro, SSR", "Serves UI and handles API requests") - Container(git_server, "Git Server", "Node.js Stream", "Smart HTTP Protocol handler for cloning/pushing") - - ContainerDb(database, "PostgreSQL", "SQL Database", "Stores user data, PR metadata, and Merge Queue state") - ContainerDb(redis, "Redis", "In-Memory", "Cache, Session Storage & Realtime Pub/Sub") - ContainerDb(s3, "Object Storage", "S3 / MinIO", "Stores raw Git packfiles and LFS objects") - } - - Rel(developer, cli, "Uses", "Terminal") - Rel(developer, web_app, "Visits", "HTTPS") - - Rel(cli, web_app, "API Calls", "JSON/REST") - Rel(cli, git_server, "Git Push/Pull", "HTTPS/Smart Protocol") - - Rel(web_app, database, "Reads/Writes", "SQL/Drizzle") - Rel(web_app, redis, "Uses", "Cache & Sessions") - - Rel(git_server, s3, "Streams Objects", "S3 API") - Rel(web_app, s3, "Reads Objects", "S3 API") - -## Component Breakdown - - - - The core of the system. Built with **Astro** in SSR mode. - - **Frontend**: Lightweight, HTML-first pages with islands architecture. - - **API**: Functional endpoints for the CLI and third-party tools. - - **Auth**: Lucia Auth for secure session management. - - - A custom implementation of the Git Smart HTTP protocol. - - **Performance**: Streams packfiles directly to/from S3 without buffering to disk. - - **Stateless**: Can be scaled horizontally across multiple nodes. - - - - **Metadata**: PostgreSQL handles the complex relationships between PRs, comments, and repos. - - **Git Data**: We treat Git objects as immutable blobs, perfect for S3-compatible storage (AWS, Cloudflare R2, MinIO). - - +OpenCodeHub is a modular monolith built for performance and self-hosting simplicity. + +## System Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CLIENTS │ +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ OPENCODEHUB PLATFORM │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Web UI │ │ REST API │ │ GraphQL Endpoint │ │ +│ │ (Astro+React)│ │ (175+ routes)│ │ (src/pages/api/) │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ Git Server │ │ SSH Server │ │ Pipeline Runner │ │ +│ │ (HTTP RPC) │ │ (ssh2) │ │ (Docker executor) │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ +┌─────────────────────────────────────────────────────────────┐ +│ PERSISTENCE & INFRASTRUCTURE │ +│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Runtime Processes + +| Process | Command | Purpose | +|---------|---------|---------| +| Main App | `npm run dev` | Web UI + API + Git HTTP | +| SSH Git | `npm run git:start` | SSH git push/pull server | +| Worker | `npm run worker:start` | Background jobs (BullMQ queues) | +| Runner | `npm run runner:start` | CI/CD pipeline execution (Docker) | + +## Tech Stack + +| Layer | Technology | +|-------|-----------| +| Framework | Astro 4.x (SSR mode, `@astrojs/node` standalone adapter) | +| UI | React 18 + Tailwind CSS + Radix UI primitives | +| Database ORM | Drizzle ORM | +| DB Drivers | PostgreSQL (pg), SQLite (better-sqlite3), LibSQL/Turso | +| Auth | JWT (jose) + bcryptjs + TOTP (otplib) + OAuth (arctic) + SSO/SAML/OIDC | +| Git | Native `git` CLI + `simple-git` + `isomorphic-git` + `nodegit` | +| SSH | `ssh2` library | +| CI/CD | Dockerode (Docker API) | +| Storage | Abstract adapter: `local` filesystem or `s3`-compatible | +| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter, Together, Bytez | +| Queue | BullMQ + Redis | +| Observability | OpenTelemetry + Prometheus + Pino | + +## Request Flow + +``` +Browser/Git CLI → Astro middleware (auth, rate limit, CSRF) + ↓ + Route handler (src/pages/api/*.ts) + ↓ + Business logic (src/lib/*.ts) + ↓ + Database (src/db/index.ts → Drizzle) +``` + +## Authentication + +- **Web**: Cookie-based JWT (`och_session`) +- **API**: Bearer token (JWT or Personal Access Token `och_...`) +- **Git HTTP**: Basic auth with PAT as password +- **Git SSH**: Public key auth against `deployKeys` table +- **SSO**: SAML and OIDC via admin-configured providers + +## Git Protocol Handling + +- **HTTP**: `src/pages/git/` routes → `src/lib/git-server.ts` → spawns `git upload-pack/receive-pack` +- **SSH**: `src/lib/ssh.ts` using `ssh2` library → spawns git processes +- **Smart HTTP**: Full pkt-line protocol implementation with sideband + +## Storage Abstraction + +All blob storage goes through `StorageAdapter`: +- `LocalStorageAdapter` — filesystem at `STORAGE_PATH` +- `S3StorageAdapter` — any S3-compatible store (AWS, MinIO, R2, Garage, Wasabi, etc.) + +Path-style addressing is enabled automatically when `STORAGE_ENDPOINT` is set. + +## Database + +`src/db/index.ts` provides a factory pattern: +- Reads `DATABASE_DRIVER` and `DATABASE_URL` +- Returns appropriate Drizzle instance +- **Production**: PostgreSQL required (schemas use `pgTable`) +- **Dev**: SQLite or Turso for simplicity + +38 schema tables cover users, repos, PRs, issues, projects, workflows, merge queue, stacked PRs, AI reviews, security policies, and more. + +## Background Workers + +The worker process (`npm run worker:start`) handles: +- BullMQ job queues for webhooks, notifications, CI triggers +- Cron jobs (garbage collection, mirror sync, notification digests) +- Email delivery via SMTP +- Real-time SSE event broadcasting diff --git a/docs-site/src/content/docs/development/contributing.md b/docs-site/src/content/docs/development/contributing.md index 2d3a2f95..54f78c26 100644 --- a/docs-site/src/content/docs/development/contributing.md +++ b/docs-site/src/content/docs/development/contributing.md @@ -2,102 +2,136 @@ title: "Contributing Guide" --- - Thank you for your interest in contributing to OpenCodeHub! We welcome contributions from everyone. -## 🛠 Local Development Setup - -To contribute code, you need to set up the project locally. +## Local Development Setup ### Prerequisites -- **Node.js**: v18 or v20 (LTS). -- **Bun**: v1.0+ (Our package manager/runner). -- **Docker**: For running the database locally. +- **Node.js** 20+ (or Bun 1.0+) +- **Docker** (for PostgreSQL and Redis) +- **Git** 2.30+ ### Step 1: Clone & Install ```bash git clone https://github.com/swadhinbiswas/OpencodeHub.git cd OpencodeHub - -# Install dependencies (fast!) -bun install +npm install ``` ### Step 2: Database Setup -Start a local Postgres and Redis instance using Docker: +Start PostgreSQL and Redis: ```bash docker-compose up -d postgres redis ``` -Copy the environment config: +Copy and configure environment: + ```bash cp .env.example .env +# The default .env.example works with docker-compose services ``` -*Note: The default `.env.example` is pre-configured to work with the docker-compose services.* -Push the schema to the database: +Push schema to database: + ```bash -bun db:push +npm run db:push ``` -### Step 3: Run the App - -Start the development server: +### Step 3: Seed & Run ```bash -bun dev +# Create admin user +bun run scripts/seed-admin.ts + +# Start dev server +npm run dev ``` -Visit `http://localhost:3000`. +Visit `http://localhost:4321`. ---- +## Running Tests -## 🧪 Running Tests - -We use **Vitest** for unit tests. Use TDD! +We use **Vitest** for unit/integration tests and **Playwright** for E2E tests. ```bash # Run all tests -bun test +npm run test + +# Run with coverage +npm run test:coverage # Run specific test file -bun test user.test.ts +npx vitest run tests/unit/auth.test.ts # Watch mode -bun test --watch +npx vitest --watch ``` ---- - -## 🎨 Code Style +**Current status: 546 tests passing across 114 test files (100% pass rate)** -We use **ESLint** and **Prettier** to enforce code style. +## Code Quality -- **Lint**: `bun lint` -- **Format**: `bun format` +```bash +npm run lint # Astro check +npm run typecheck # TypeScript check +npm run test # Test suite +``` ### Commit Messages -We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification. +We follow [Conventional Commits](https://www.conventionalcommits.org/): - `feat: add new login page` - `fix: resolve crash on startup` - `docs: update troubleshooting guide` +- `refactor: simplify permission check` +- `test: add coverage for merge queue` ---- +## Submitting a Pull Request + +1. **Fork** the repository +2. Create a **feature branch**: `git checkout -b feat/my-feature` +3. Make your changes with tests +4. Run `npm run test` and `npm run typecheck` to verify +5. Commit with a conventional commit message +6. Push to your fork +7. Open a **Pull Request** against `main` +8. Wait for CI checks to pass + +## Project Structure + +``` +src/ +├── pages/ # Astro file-based routing +│ ├── api/ # REST API routes (175+ endpoints) +│ └── [owner]/[repo]/ # Repository pages +├── lib/ # Core business logic (130+ modules) +├── db/ # Database layer (Drizzle ORM) +│ ├── schema/ # 38 schema table definitions +│ └── index.ts # DB connection factory +├── components/ # React components +├── runner/ # CI runner (Docker executor) +└── middleware.ts # Auth, rate limit, CSRF + +cli/ +├── src/commands/ # CLI command groups (20+) +└── src/lib/ # CLI utilities + +docs-site/ # This documentation site (Starlight) +``` -## 🚀 Submitting a Pull Request +## Areas for Contribution -1. **Fork** the repository. -2. Create a **feature branch**: `git checkout -b feat/my-feature`. -3. Commit your changes. -4. Push to your fork. -5. Open a **Pull Request**. -6. Wait for CI checks to pass. -7. A team member will review your code! +- **CI/CD Features**: Matrix builds, caching, workflow templates +- **Package Registry**: npm and Docker registry enhancements +- **AI Review**: New provider integrations, review quality improvements +- **CLI**: New commands, better error messages +- **Tests**: Increase coverage, especially for edge cases +- **Documentation**: Tutorials, guides, API examples +- **Accessibility**: WCAG compliance, keyboard navigation -Thank you for helping build OpenCodeHub! ❤️ +Thank you for helping build OpenCodeHub! diff --git a/docs-site/src/content/docs/development/database-schema.md b/docs-site/src/content/docs/development/database-schema.md index 1c45662e..4c34ea8e 100644 --- a/docs-site/src/content/docs/development/database-schema.md +++ b/docs-site/src/content/docs/development/database-schema.md @@ -2,15 +2,60 @@ title: "Database Schema" --- +OpenCodeHub uses Drizzle ORM with 38 schema tables defined in `src/db/schema/`. -The database schema is defined using Drizzle ORM in `drizzle/schema.ts`. +## Schema Files -## Core Tables +| File | Tables | Purpose | +|------|--------|---------| +| `users.ts` | `users` | User accounts, passwords, OAuth, 2FA, admin flags | +| `repositories.ts` | `repositories` | Git repos, visibility, disk paths, settings | +| `pull-requests.ts` | `pullRequests` | PRs, states, branches, merge status | +| `issues.ts` | `issues` | Issue tracking | +| `projects.ts` | `projects` | Project boards, columns, cards | +| `stacked-prs.ts` | `prStacks`, `prStackEntries` | Stacked PR relationships | +| `merge-queue.ts` | `mergeQueue` | Queue entries with priority, CI status, attempts | +| `workflows.ts` | `workflows`, `workflowRuns`, `workflowJobs`, `workflowSteps` | CI/CD pipeline execution | +| `pipeline-runners.ts` | `pipelineRunners` | Registered CI runners | +| `ai-reviews.ts` | `aiReviews` | AI code review results | +| `ai-review-rules.ts` | `aiReviewRules` | AI review rule configurations | +| `developer-metrics.ts` | `developerMetrics` | PR metrics, cycle time | +| `branch-protection.ts` | `branchProtection`, `reviewRequirements`, `requiredStatusChecks` | Branch rules | +| `webhooks.ts` | `webhooks` | Outbound webhook configs | +| `automations.ts` | `automations` | Workflow automation rules | +| `slack-integration.ts` | `slackIntegration` | Slack notification configs | +| `organizations.ts` | `organizations` | Organizations | +| `teams.ts` | `teams` | Team membership | +| `roles.ts` | `roles` | Role-based access control | +| `activity.ts` | `activity` | Activity/audit log | +| `security.ts` | `security` | Security scan results | +| `security-policies.ts` | `securityPolicies` | Security policy definitions | +| `sso.ts` | `sso` | SSO/OIDC configuration | +| `wiki.ts` | `wiki` | Repository wiki pages | +| `deploy-keys.ts` | `deployKeys` | SSH deploy keys | +| `path-permissions.ts` | `pathPermissions` | File-level access control | +| `pr-states.ts` | `prStates` | Custom PR states | +| `issue-statuses.ts` | `issueStatuses` | Custom issue statuses | +| `custom-fields.ts` | `customFields` | Custom issue/project fields | +| `auto-merge-rules.ts` | `autoMergeRules` | Auto-merge configuration | +| `external-ci.ts` | `externalCIConfigs`, `externalBuilds` | External CI integration | +| `review-rules.ts` | `reviewRules` | Review requirements and reviewer routing | +| `integrations.ts` | `integrations` | Code quality, dashboard, deployment integrations | +| `packages.ts` | `packages` | Package registry metadata | +| `inbox-sections.ts` | `inboxSections` | Custom inbox sections | +| `notification-preferences.ts` | `notificationPreferences` | Per-user notification settings | +| `system-config.ts` | `systemConfig` | System-wide configuration | -- **users**: Stores user accounts, hashed passwords, and profile info. -- **repositories**: Stores repository metadata (owner, name, visibility). -- **pull_requests**: Stores PR data, status, and description. -- **sessions**: Stores active login sessions (if using database sessions). +## Relationships + +Key relationships between tables: +- `repositories` → `users` (owner) +- `pullRequests` → `repositories` (repo) + `users` (author) +- `prStacks` → `repositories` + `prStackEntries` → `pullRequests` +- `mergeQueue` → `pullRequests` + `repositories` +- `workflowRuns` → `workflows` + `repositories` +- `issues` → `repositories` + `users` +- `projects` → `repositories` ## Migrations @@ -26,7 +71,20 @@ npm run db:generate npm run db:migrate ``` -**Push schema (Prototyping):** +**Push schema (prototyping — no migration files):** ```bash npm run db:push ``` + +**Open Drizzle Studio GUI:** +```bash +npm run db:studio +``` + +## Database Support + +| Driver | Use Case | Notes | +|--------|----------|-------| +| `postgres` | Production | Required. All schemas use `pgTable` | +| `sqlite` | Development | Simple local setup | +| `turso` | Edge (Vercel) | LibSQL for serverless deployments | diff --git a/docs-site/src/content/docs/development/local-dev-setup.md b/docs-site/src/content/docs/development/local-dev-setup.md index 32da486c..9205319c 100644 --- a/docs-site/src/content/docs/development/local-dev-setup.md +++ b/docs-site/src/content/docs/development/local-dev-setup.md @@ -5,48 +5,102 @@ description: "Set up a productive local dev environment for OpenCodeHub." ## Prerequisites -- Node.js 18+ (project `engines` specify Node 20+ for runtime) -- Git 2.30+ -- PostgreSQL/MySQL/SQLite (PostgreSQL recommended) +- **Node.js** 20+ (or Bun 1.0+) +- **Git** 2.30+ +- **PostgreSQL** 14+ (recommended) or SQLite (for quick setup) +- **Redis** 7+ (optional for local dev, required for background workers) -## Install dependencies +## Quick Setup ```bash -npm install -``` +# Clone the repository +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub -## Environment +# Install dependencies +npm install -```bash +# Copy environment config cp .env.example .env -``` -Set required values like `JWT_SECRET`, `SESSION_SECRET`, `INTERNAL_HOOK_SECRET`, and `SITE_URL`. +# Edit .env with your settings (at minimum, set the secrets) +# Generate secrets with: openssl rand -base64 32 +``` ## Database ```bash +# Push schema to database (creates tables) npm run db:push + +# Or generate and run migrations +npm run db:generate +npm run db:migrate + +# Open Drizzle Studio GUI +npm run db:studio +``` + +For SQLite (simplest setup), set in `.env`: +``` +DATABASE_DRIVER=sqlite +DATABASE_URL=file:./data/opencodehub.db ``` -## Seed admin user +## Seed Admin User ```bash bun run scripts/seed-admin.ts ``` -## Run dev server +## Start Development ```bash +# Main app (Web UI + API + Git HTTP) npm run dev + +# SSH Git server (optional, for ssh:// clone/push) +npm run git:start + +# Background worker (optional, for webhooks/notifications) +npm run worker:start + +# CI runner (optional, for pipeline execution) +npm run runner:start +``` + +The dev server runs at `http://localhost:4321`. + +## Useful Scripts + +| Command | Purpose | +|---------|---------| +| `npm run dev` | Start Astro dev server | +| `npm run build` | Production build | +| `npm run test` | Run Vitest test suite | +| `npm run test:coverage` | Run tests with coverage | +| `npm run lint` | Run Astro check | +| `npm run typecheck` | TypeScript type checking | +| `npm run db:push` | Push schema to database | +| `npm run db:generate` | Generate Drizzle migrations | +| `npm run db:migrate` | Run migrations | +| `npm run db:studio` | Open Drizzle Studio | +| `npm run git:start` | Start SSH git server | +| `npm run worker:start` | Start background worker | +| `npm run runner:start` | Start CI runner | + +## Docker Compose (Alternative) + +For a full stack with PostgreSQL and Redis: + +```bash +docker-compose up -d +docker-compose exec app bun run scripts/seed-admin.ts ``` -Open the app at `http://localhost:3000`. +This starts the app on port 4321 and SSH on port 2222. -## Useful scripts +## IDE Setup -- `npm run db:generate` -- `npm run db:migrate` -- `npm run db:studio` -- `npm run lint` -- `npm run test` +- **VS Code**: Install the Astro extension for syntax highlighting +- **Recommended extensions**: Tailwind CSS IntelliSense, ESLint diff --git a/docs-site/src/content/docs/development/release-gates.md b/docs-site/src/content/docs/development/release-gates.md new file mode 100644 index 00000000..b454efd2 --- /dev/null +++ b/docs-site/src/content/docs/development/release-gates.md @@ -0,0 +1,21 @@ +# Release Gates + +This document outlines the required gates that must be passed before any code can be merged into the default branch. + +## 1. Continuous Integration (CI) +All pull requests must pass the following CI checks: +- **Linting**: `npm run lint` must exit with 0. +- **Typechecking**: `npm run typecheck` must exit with 0. +- **Tests**: Unit, integration, and E2E tests (`npm run test`) must all pass. + +## 2. Security Scans +- **Secret Scanning**: Commits must be scanned for leaked secrets. +- **Dependency Audit**: `npm audit` must not report vulnerabilities above the defined threshold. +- **Authorization Regression**: Core RBAC and permissions tests must pass. + +## 3. Documentation Parity +Any feature addition must include updates to both documentation stacks: +- The Markdown docs in `docs/` +- The Starlight site in `docs-site/src/content/docs/` + +Features will not be merged if their documentation is missing or incomplete. diff --git a/docs-site/src/content/docs/features/ai-review.md b/docs-site/src/content/docs/features/ai-review.md index 4d1a9637..4b4542e6 100644 --- a/docs-site/src/content/docs/features/ai-review.md +++ b/docs-site/src/content/docs/features/ai-review.md @@ -2,67 +2,60 @@ title: "AI Code Review" --- -> Get instant, intelligent code reviews powered by GPT-4 or Claude - AI Code Review uses advanced language models to automatically review your pull requests, catching bugs, security issues, and code quality problems before they reach production. -## 📖 Table of Contents - -- [Overview](#overview) -- [Setup](#setup) - - [Configure AI Provider](#configure-ai-provider) - - [Enable for Repository](#enable-for-repository) -- [Using AI Review](#using-ai-review) -- [Understanding Review Output](#understanding-review-output) -- [Customizing Reviews](#customizing-reviews) -- [Cost Considerations](#cost-considerations) -- [Best Practices](#best-practices) -- [Troubleshooting](#troubleshooting) +## Supported Providers ---- +OpenCodeHub supports 10+ AI providers. Configure one or more in your `.env` file: -## Overview +| Provider | Models | API Key Env Var | +|----------|--------|-----------------| +| **OpenAI** | GPT-4, GPT-4o, GPT-4 Turbo | `OPENAI_API_KEY` | +| **Anthropic** | Claude 3.5 Sonnet, Claude 3 Opus | `ANTHROPIC_API_KEY` | +| **Google** | Gemini 1.5 Pro, Gemini Flash | `GOOGLE_AI_API_KEY` | +| **Groq** | Llama 3, Mixtral (fast inference) | `GROQ_API_KEY` | +| **OpenRouter** | Multi-model gateway | `OPENROUTER_API_KEY` | +| **Together** | Llama, Mixtral, CodeLlama | `TOGETHER_API_KEY` | +| **Bytez** | Various models | `BYTEZ_API_KEY` | +| **Ollama** | Any local model (Llama, CodeLlama, etc.) | No key needed | +| **External Agent** | Custom webhook | `EXTERNAL_AGENT_WEBHOOK_URL` | -### What AI Review Catches +:::tip[Recommended] +For best results, use **GPT-4o** or **Claude 3.5 Sonnet**. For fast/cheap reviews, use **Groq** or **Ollama** with a local model. +::: -🔒 **Security Vulnerabilities** +## What AI Review Catches -- SQL injection risks -- XSS (Cross-Site Scripting) vectors -- Authentication bypasses -- Secrets/credentials in code +**Security Vulnerabilities** +- SQL injection, XSS, CSRF +- Authentication/authorization bypasses +- Exposed secrets or credentials - Insecure dependencies -⚡ **Performance Issues** - +**Performance Issues** - N+1 database queries -- Inefficient algorithms (O(n²) → O(n log n)) +- Inefficient algorithms - Memory leaks -- Blocking operations in async code - Missing indexes -📝 **Code Quality** - +**Code Quality** - Best practice violations - Code smells (long functions, deep nesting) - Incomplete error handling - Missing input validation -- Poor naming conventions - -🐛 **Potential Bugs** +**Potential Bugs** - Null/undefined pointer exceptions - Race conditions - Off-by-one errors -- Logic flaws - Type mismatches -### How It Works +## How It Works ``` 1. Developer opens/updates PR ↓ -2. OpenCodeHub sends diff to AI +2. OpenCodeHub sends diff to AI provider ↓ 3. AI analyzes code changes ↓ @@ -71,526 +64,151 @@ AI Code Review uses advanced language models to automatically review your pull r 5. Developer addresses feedback ``` -**Privacy note:** Code is sent to AI provider (OpenAI/Anthropic). Use on-premise models for sensitive code. - ---- +**Privacy note:** Code is sent to the configured AI provider. Use Ollama or the external agent for sensitive codebases. ## Setup -### Configure AI Provider - -**Option 1: OpenAI (GPT-4)** - -1. Get API key from [platform.openai.com](https://platform.openai.com/api-keys) - -2. Add to `.env`: +### Step 1: Configure AI Provider +**OpenAI:** ```bash - AI_PROVIDER=openai - OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx - OPENAI_MODEL=gpt-4-turbo-preview # or gpt-4, gpt-3.5-turbo +OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx ``` -3. Restart OpenCodeHub - -**Option 2: Anthropic (Claude)** - -1. Get API key from [console.anthropic.com](https://console.anthropic.com/) - -2. Add to `.env`: - +**Anthropic:** ```bash - AI_PROVIDER=anthropic - ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx - ANTHROPIC_MODEL=claude-3-opus-20240229 # or claude-3-sonnet +ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx ``` -3. Restart OpenCodeHub - -**Option 3: Azure OpenAI** - +**Google Gemini:** ```bash -AI_PROVIDER=azure-openai -AZURE_OPENAI_API_KEY=xxxxx -AZURE_OPENAI_ENDPOINT=https://yourinstance.openai.azure.com/ -AZURE_OPENAI_DEPLOYMENT=your-deployment-name +GOOGLE_AI_API_KEY=xxxxxxxxxxxxx ``` -**Option 4: Self-Hosted (Coming Soon)** - -OpenCodeHub will support local models via Ollama: +**Groq (fast, cheap):** +```bash +GROQ_API_KEY=gsk_xxxxxxxxxxxxx +``` +**Ollama (local, private):** ```bash -AI_PROVIDER=ollama -OLLAMA_ENDPOINT=http://localhost:11434 -OLLAMA_MODEL=codellama:13b +# No env var needed — configure in Settings → AI +# Just install Ollama: https://ollama.com +# Pull a model: ollama pull codellama:13b ``` -### Enable for Repository +**Multiple providers:** Configure all keys. The system picks the best available provider. -**Via Web UI:** +### Step 2: Enable for Repository -1. Navigate to **Repository → Settings → AI Review** -2. Toggle **"Enable AI Code Review"** -3. Configure settings: - - **Trigger**: `On PR open`, `On push`, or `Manual only` - - **Auto-comment**: Post comments automatically - - **Severity threshold**: `All`, `Warning+`, or `Critical only` - - **Max files**: Limit review to N files (cost control) +**Via Web UI:** +1. Go to **Repository → Settings → AI Review** +2. Toggle **Enable AI Code Review** +3. Configure trigger, severity threshold, and max files 4. Click **Save** **Via API:** - ```bash -curl -X PATCH https://git.yourcompany.com/api/repos/123/ai-review \ +curl -X PATCH https://git.yourcompany.com/api/repos/OWNER/REPO/settings \ -H "Authorization: Bearer $TOKEN" \ - -d '{ - "enabled": true, - "trigger": "on_pr_open", - "auto_comment": true, - "severity_threshold": "warning" - }' + -d '{ "aiReview": { "enabled": true } }' ``` **Via CLI:** - ```bash -och repo config ai-review --enable \ - --trigger on_pr_open \ - --auto-comment +och repo config ai-review --enable ``` ---- - ## Using AI Review ### Automatic Reviews -If enabled, AI reviews trigger automatically: - -```bash -# 1. Create PR -git push origin feature-branch -# → AI review starts automatically - -# 2. Update PR -git push origin feature-branch -# → AI re-reviews changed files only -``` - -You'll see: - -- ✅ **Status check**: "AI Review: In Progress" → "AI Review: Complete" -- 💬 **Inline comments**: On specific lines with issues -- 📊 **Summary comment**: Overall assessment +When enabled, AI reviews trigger automatically when PRs are opened or updated. ### Manual Reviews -Request AI review on-demand: - -**Via Web UI:** - -1. Open PR page -2. Click **"Request AI Review"** button -3. Wait ~30-60 seconds -4. Review appears as comments - -**Via CLI:** +**Web UI:** Open PR page → Click "Request AI Review" +**CLI:** ```bash -# Review specific PR -och review ai 125 - -# Wait for the review to complete -och review ai 125 --wait +och review ai ``` -**Via API:** - +**API:** ```bash curl -X POST https://git.yourcompany.com/api/repos/OWNER/REPO/pulls/125/ai-review \ -H "Authorization: Bearer $TOKEN" ``` ---- - ## Understanding Review Output ### Severity Levels -AI assigns severity to each finding: - -| Severity | Icon | Meaning | Example | -| --------------- | ---------- | -------------------------------------- | ----------------------------------------- | -| 🔴 **CRITICAL** | Must fix | Security vulnerability, data loss risk | SQL injection, exposed API key | -| 🟡 **WARNING** | Should fix | Performance issue, bad practice | N+1 query, missing error handling | -| 🔵 **INFO** | Consider | Suggestion, refactoring opportunity | Better variable name, code simplification | - -### Example Review Comment - -````markdown -🔴 CRITICAL: SQL Injection Vulnerability - -**File:** `src/api/users.ts` -**Line:** 45 - -**Issue:** -User input is directly concatenated into SQL query without sanitization. - -**Current Code:** - -```typescript -const query = `SELECT * FROM users WHERE email = '${userEmail}'`; -``` -```` - -**Risk:** -An attacker could inject malicious SQL: - -``` -userEmail = "' OR '1'='1" -→ Query becomes: SELECT * FROM users WHERE email = '' OR '1'='1' -→ Returns ALL users (authentication bypass) -``` - -**Fix:** -Use parameterized queries or ORM: - -```typescript -const query = `SELECT * FROM users WHERE email = ?`; -const results = await db.query(query, [userEmail]); - -// Or with ORM: -const user = await db.users.findOne({ email: userEmail }); -``` - -**References:** - -- [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) -- [Node.js SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) - -```` +| Severity | Meaning | Example | +|----------|---------|---------| +| **CRITICAL** | Must fix — security/data loss | SQL injection, exposed key | +| **WARNING** | Should fix — perf/best practice | N+1 query, missing error handling | +| **INFO** | Consider — suggestion | Better variable name | ### Summary Comment -AI posts an overall summary: - -```markdown -## 🤖 AI Code Review Summary +AI posts an overall summary with statistics, critical issues, and recommendations. -**Overall Assessment:** ⚠️ Needs Attention +### Custom Rules -### Statistics -- Files reviewed: 8 -- Critical issues: 2 -- Warnings: 5 -- Info: 3 - -### Critical Issues -1. 🔴 SQL Injection in `src/api/users.ts:45` -2. 🔴 Hardcoded API key in `src/config.ts:12` - -### Recommendations -1. Add input validation to all API endpoints -2. Move secrets to environment variables -3. Consider adding integration tests for auth flows - -### Positive Notes -- ✅ Good error handling in payment service -- ✅ Well-structured database migrations -- ✅ Comprehensive TypeScript types - -**Estimated fix time:** 2-3 hours -```` - ---- - -## Customizing Reviews - -### Review Rules - -Create `.opencodehub/ai-review.yml` in your repository: +Create `.opencodehub/ai-review.yml` in your repo: ```yaml -# AI Review Configuration - -# Which files to review include: - "src/**/*.ts" - - "src/**/*.tsx" - - "lib/**/*.js" - -# Which files to skip exclude: - "**/*.test.ts" - - "**/*.spec.ts" - - "**/generated/**" - "dist/**" -# Focus areas (weighted 1-10) focus: - security: 10 # Highest priority + security: 10 performance: 8 bugs: 8 code_quality: 5 - documentation: 3 - -# Severity thresholds -report: - critical: always - warning: always - info: on_request # Only if --verbose flag used -# Custom rules custom_rules: - name: "No console.log in production" - pattern: 'console\\.log\\(' + pattern: 'console\.log\(' severity: warning message: "Remove console.log before merging" - - - name: "Require error handling" - pattern: 'await.*\\(.*\\)' - check: must_have_try_catch - severity: warning - -# AI instructions -instructions: > - Pay special attention to: - 1. Authentication and authorization logic - 2. Database query efficiency - 3. Input validation - 4. Proper error handling - - Our coding standards: - - TypeScript strict mode - - Functional programming preferred - - Maximum function length: 50 lines - - Maximum cyclomatic complexity: 10 ``` -### Custom Prompts - -Use **Settings → AI Review Rules** to add repository‑specific prompts and checks. Rules are applied automatically when AI reviews run. - -### Language-Specific Reviews - -AI automatically adapts to programming language: - -- **TypeScript/JavaScript**: Type safety, async/await, React best practices -- **Python**: PEP 8, type hints, Django/Flask patterns -- **Go**: Go idioms, error handling, concurrency -- **Rust**: Borrow checker, unsafe code, idiomatic Rust -- **Java**: Design patterns, Spring best practices, thread safety - ---- +Or use **Settings → AI Review Rules** in the web UI. ## Cost Considerations -### Pricing - -AI review costs depend on provider and model: - -**OpenAI (GPT-4 Turbo):** - -- ~$0.01 per 1K tokens (input) -- ~$0.03 per 1K tokens (output) -- **Typical PR**: 500-2000 tokens = **$0.02-0.10 per review** - -**Anthropic (Claude 3 Opus):** - -- ~$0.015 per 1K tokens (input) -- ~$0.075 per 1K tokens (output) -- **Typical PR**: **$0.03-0.15 per review** - -**Example monthly cost:** - -- 50 PRs/month × $0.05/review = **$2.50/month** -- 500 PRs/month × $0.05/review = **$25/month** - -### Cost Optimization - -To reduce cost: - -- Keep PRs small and focused. -- Avoid very large diffs in a single review. +| Provider | Approx Cost per Review | +|----------|----------------------| +| Groq | Free (rate limited) | +| Ollama | Free (local) | +| OpenAI GPT-4o | ~$0.02-0.10 | +| Anthropic Claude | ~$0.03-0.15 | +| Google Gemini | ~$0.01-0.05 | -**3. Skip test files:** - -```yaml -exclude: - - "**/*.test.*" - - "**/*.spec.*" -``` - -**4. Review only on-request:** - -```yaml -trigger: manual # Don't auto-review every push -``` - -**5. Use severity threshold:** - -```yaml -severity_threshold: warning # Skip "info" level comments -``` - ---- - -## Best Practices - -### 💡 When to Use AI Review - -**✅ Good Use Cases:** - -- Security-sensitive code (auth, payments) -- Complex algorithms -- Database queries -- API endpoints -- Third-party integrations - -**❌ Less Useful:** - -- Generated code -- Configuration files -- Simple refactoring -- Documentation-only changes - -### 💡 Responding to AI Feedback - -**1. Don't auto-fix everything** - -- AI can be wrong -- Context matters -- Use judgment - -**2. Engage in discussion** - -```markdown -@ai-review That's a good catch, but in this case we're using a -trusted source and the data is already sanitized earlier in the pipeline. - -See line 23 where we validate input. -``` - -**3. Track false positives** - -```yaml -# .opencodehub/ai-review.yml -ignore: - - file: src/utils/legacy.ts - reason: "Legacy code, requires major refactor" - until: 2024-12-31 -``` - -### 💡 Combining with Human Review - -AI complements, doesn't replace humans: - -``` -1. AI reviews first (catches obvious issues) -2. Developer fixes critical/warnings -3. Human reviewer approves -``` - -This workflow: - -- Reduces human reviewer burden -- Catches more bugs -- Faster feedback cycles - ---- +**Cost optimization:** +- Keep PRs small and focused +- Skip test files in review config +- Use severity threshold to filter info-level comments +- Use Groq or Ollama for routine reviews, GPT-4/Claude for critical code ## Troubleshooting -### "AI review taking too long" - -**Cause:** Large PR (many files/lines). - -**Solution:** - -```bash -# Check review status -och review status 125 - -# Retry the review -och review ai 125 -``` - -### "AI review failed" - -**Causes:** - -1. API key invalid/expired -2. Rate limit exceeded -3. Timeout (very large PR) - -**Solutions:** +**AI review taking too long:** Large PR (many files). Split into smaller PRs. -```bash -# 1. Verify API key -echo $OPENAI_API_KEY - -# 2. Check rate limits -curl https://api.openai.com/v1/usage \ - -H "Authorization: Bearer $OPENAI_API_KEY" - -# 3. Split large PR or review manually. -``` - -### "Too many false positives" - -**Solution:** Use **Settings → AI Review Rules** to refine prompts and reduce false positives. - -### "Missing obvious bugs" - -**Cause:** AI missed context or edge case. - -**Solution:** Add custom instructions in **Settings → AI Review Rules** so reviews incorporate your conventions. - ---- - -## Advanced: CI/CD Integration - -### Block merge on critical findings +**AI review failed:** Check API key is valid. Verify provider is configured in Settings → AI. -```yaml -# .github/workflows/ai-review.yml -name: AI Code Review -on: [pull_request] - -jobs: - ai-review: - runs-on: ubuntu-latest - steps: - - name: Run AI Review - run: och review ai ${{ github.event.pull_request.number }} - env: - OCH_TOKEN: ${{ secrets.OCH_TOKEN }} - - - name: Check for critical issues - run: | - REVIEW=$(curl -s \ - -H "Authorization: Bearer $OCH_TOKEN" \ - https://git.yourcompany.com/api/repos/OWNER/REPO/pulls/${{ github.event.pull_request.number }}/ai-review/latest) - SEVERITY=$(echo "$REVIEW" | jq -r '.data.overallSeverity') - if [ "$SEVERITY" = "critical" ]; then - echo "❌ Critical issues found" - exit 1 - fi -``` +**Too many false positives:** Refine rules in Settings → AI Review Rules. Add custom instructions. ---- +**Missing obvious bugs:** Add custom instructions describing your conventions and patterns. ## See Also -- [Stacked Pull Requests](stacked-prs.md) -- [Smart Merge Queue](merge-queue.md) -- [CI/CD Actions](ci-actions.md) - ---- - -## Need Help? - -- 💬 [Discord Community](https://discord.gg/opencodehub) -- 🐛 [Report Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) -- 📖 [Full Documentation](/) +- [Stacked PRs](/features/stacked-prs/) +- [Merge Queue](/features/merge-queue/) +- [CI/CD Actions](/features/ci-actions/) +- [Configuration Reference](/administration/configuration/#ai-providers) diff --git a/docs-site/src/content/docs/getting-started/first-repository.md b/docs-site/src/content/docs/getting-started/first-repository.md index 24dee0ba..f7df97d4 100644 --- a/docs-site/src/content/docs/getting-started/first-repository.md +++ b/docs-site/src/content/docs/getting-started/first-repository.md @@ -2,67 +2,108 @@ title: "Create Your First Repository" --- - This guide walks you through the complete lifecycle of a repository in OpenCodeHub. -## Step 1: Creation - -1. Click the **+** button in the top navbar and select **New Repository**. -2. **Owner**: Select yourself or an organization. -3. **Repository Name**: Enter `hello-world`. -4. **Description**: "My first project on OpenCodeHub". -5. **Visibility**: Select **Public**. -6. **Initialize**: Check **Initialize with README**. +## Step 1: Create the Repository -Click **Create Repository**. You'll be taken to your new repository's home page. +1. Click the **+** button in the top navbar → **New Repository** +2. **Owner**: Select yourself or an organization +3. **Repository Name**: Enter `hello-world` +4. **Description**: "My first project on OpenCodeHub" +5. **Visibility**: Select **Public** +6. **Initialize**: Check **Initialize with README** -## Step 2: Cloning +Click **Create Repository**. -To work on your computer, you need to clone the repository. +## Step 2: Clone ### SSH (Recommended) -Ensure you have added your SSH key in **Settings > SSH Keys**. + +Add your SSH key in **Settings → SSH Keys** first. ```bash -git clone git@your-instance.com:username/hello-world.git +git clone ssh://git@your-server:2222/username/hello-world.git ``` ### HTTPS + ```bash -git clone https://your-instance.com/username/hello-world.git +git clone https://git.yourcompany.com/username/hello-world.git ``` -## Step 3: Making Changes +For HTTPS, you'll need a Personal Access Token. Generate one at **Settings → Tokens**. -Open the folder in your favorite editor (VS Code, etc.). +## Step 3: Make Changes ```bash cd hello-world -``` -Modify `README.md`: -```markdown -# Hello World -This is my first change! -``` +# Edit files +echo "# Hello World\n\nThis is my first change!" > README.md -Commit the change: -```bash +# Commit git add README.md git commit -m "Update README" ``` -## Step 4: Pushing - -Push your changes back to OpenCodeHub: +## Step 4: Push ```bash git push origin main ``` -(Or use `och push` if you installed the CLI). -## Step 5: Viewing Changes +Refresh the repository page to see your changes. + +## Step 5: Create a Pull Request + +1. Create a feature branch: +```bash +git checkout -b feat/add-feature +``` + +2. Make changes and push: +```bash +echo "New feature" > feature.txt +git add feature.txt +git commit -m "Add new feature" +git push -u origin feat/add-feature +``` + +3. In the web UI, go to **Pull Requests** → **New Pull Request** +4. Select `feat/add-feature` as source, `main` as target +5. Add a title and description +6. Click **Create Pull Request** + +## Step 6: Review and Merge + +1. Add reviewers on the PR page +2. Reviewers can approve or request changes +3. Once approved, click **Merge** or add to the **Merge Queue** + +## Step 7: Explore Features + +### Wiki +Go to the **Wiki** tab to create documentation for your project. + +### Issues +Go to **Issues** → **New Issue** to track bugs and feature requests. + +### Projects +Go to **Projects** to create a kanban board for task management. + +### Actions +Go to **Actions** to view CI/CD pipeline runs. Create `.github/workflows/*.yml` files to set up automated builds. + +### Settings +Go to **Settings** to configure: +- **Branch Protection** — Require reviews, status checks +- **Collaborators** — Add team members +- **Webhooks** — Integrations with external services +- **Deploy Keys** — SSH keys for automated deployments -Refresh your repository page in the browser. You should see your updated `README.md` displayed and the latest commit listed. +## Next Steps -Congratulations! You've successfully hosted code on OpenCodeHub. +- **[Stacked PRs](/features/stacked-prs/)** — Break large changes into reviewable stacks +- **[AI Code Review](/features/ai-review/)** — Get automated code feedback +- **[CI/CD](/features/ci-actions/)** — Set up automated pipelines +- **[CLI](/reference/cli-overview/)** — Manage everything from the terminal diff --git a/docs-site/src/content/docs/getting-started/installation.md b/docs-site/src/content/docs/getting-started/installation.md index 71cfc468..c20e8005 100644 --- a/docs-site/src/content/docs/getting-started/installation.md +++ b/docs-site/src/content/docs/getting-started/installation.md @@ -2,10 +2,9 @@ title: "Installation Guide" --- +OpenCodeHub is a self-hosted Git platform. You can run it via **Docker** (recommended) or **Node.js** (for development). -OpenCodeHub allows you to host your own GitHub-like platform. You can run it via **Docker** (recommended for production) or **Node.js** (for development/custom setups). - -## 📋 System Requirements +## System Requirements | Component | Minimum | Recommended | |-----------|---------|-------------| @@ -14,53 +13,57 @@ OpenCodeHub allows you to host your own GitHub-like platform. You can run it via | **Disk** | 10GB | 50GB SSD | | **OS** | Linux (Ubuntu/Debian) | Linux | ---- - -## ⚡ 1-Click Installation (Recommended) - -The easiest way to install OpenCodeHub on a VPS, NAS, or edge device is using the automated installation script. This script automatically handles Docker setup, environment variables, database generation, and configuring the unified `DATA_DIR`. +## 1-Click Installation (Recommended) ```bash curl -sSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash ``` The script will: -1. Ensure Docker and Docker Compose are installed. -2. Clone the latest stable release. -3. Automatically configure a unified `/data` mount point (`DATA_DIR`) for all your databases, git repos, and storage to easily enable painless backups. -4. Prompt you to create an initial Admin user account. - -### Manual Setup (Advanced) +1. Ensure Docker and Docker Compose are installed +2. Clone the latest stable release +3. Configure a unified `DATA_DIR` for databases, git repos, and storage +4. Prompt you to create an initial admin user -If you prefer to configure Docker Compose manually: +## Manual Docker Setup -1. Clone the repository: ```bash +# Clone git clone https://github.com/swadhinbiswas/OpencodeHub.git -cd OpenCodeHub -``` +cd OpencodeHub -2. Configure environment: -```bash +# Configure cp .env.example .env -``` -Open `.env` and set your `DATA_DIR` (e.g., `DATA_DIR=/home/user/opencodehub-data`). The system will automatically map the database, SSH keys, git repositories, and object storage paths to this unified folder. +# Edit .env — at minimum, change all secrets! -3. Start services: -```bash +# Start docker-compose up -d + +# Create admin user +docker-compose exec app bun run scripts/seed-admin.ts ``` -4. Initialize the database and admin user: +The app runs on **port 4321** (HTTP) and **port 2222** (SSH git). + +:::caution[Change Secrets] +Before going live, generate new secrets for all `*_SECRET` and `*_KEY` variables: ```bash -bun run scripts/seed-admin.ts +openssl rand -base64 32 ``` +::: ---- +## Docker Compose Services -## 🛠 Nginx Reverse Proxy (SSL) +| Service | Port | Purpose | +|---------|------|---------| +| `app` | 4321, 2222 | Main application + SSH git | +| `postgres` | 5432 | PostgreSQL database | +| `redis` | 6379 | Redis cache/queues | +| `runner` | — | CI/CD Docker runner | -In production, you **must** use HTTPS. Here is a standard Nginx configuration. +## Nginx Reverse Proxy (SSL) + +In production, use HTTPS. Standard Nginx config: ```nginx server { @@ -80,7 +83,7 @@ server { client_max_body_size 500M; location / { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -89,21 +92,55 @@ server { } ``` ---- +See [Nginx Deployment](/administration/deploy-nginx/) for the full config with WebSocket support and Git optimizations. -## ✅ Production Checklist +## SSH Git Access -Before going live to users: +The SSH git server starts automatically on port 2222. Clone with: -- [ ] **Secrets Rotated**: Default secrets replaced with strong random strings. -- [ ] **HTTPS Enabled**: SSL certificate configured via Nginx/Caddy. -- [ ] **Rate Limiting**: `RATE_LIMIT_*` env vars adjusted for expected load. -- [ ] **Monitoring**: Grafana/Sentry configured for error tracking ([Guide](../administration/monitoring.md)). +```bash +git clone ssh://git@your-server:2222/owner/repo.git +``` ---- +The host key is auto-generated at `GIT_SSH_HOST_KEY` on first start. -## Next Steps +## Node.js Setup (Development) + +```bash +# Install dependencies +npm install + +# Configure +cp .env.example .env + +# Push schema to database +npm run db:push + +# Create admin user +bun run scripts/seed-admin.ts + +# Start dev server +npm run dev +``` + +See [Local Dev Setup](/development/local-dev-setup/) for details. -Now that you have OpenCodeHub up and running, let's look around: +## Production Checklist + +Before going live: + +- [ ] All secrets rotated (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, etc.) +- [ ] HTTPS enabled via Nginx/Caddy +- [ ] PostgreSQL used (not SQLite) +- [ ] Redis configured for distributed locking +- [ ] Rate limiting enabled (`RATE_LIMIT_ENABLED=true`) +- [ ] Email configured (SMTP) for notifications +- [ ] Storage backend configured (S3 recommended for multi-node) +- [ ] Backup strategy configured +- [ ] Monitoring set up (see [Monitoring](/administration/monitoring/)) + +## Next Steps -👉 **[Quick Start Guide](quick-start.md)** +- **[Quick Start Guide](/getting-started/quick-start/)** — Navigate the dashboard and create your first repo +- **[Configuration Reference](/administration/configuration/)** — All environment variables +- **[Docker Deployment](/administration/deploy-docker/)** — Production Docker setup diff --git a/docs-site/src/content/docs/getting-started/quick-start.md b/docs-site/src/content/docs/getting-started/quick-start.md index 1f5750e4..c7087d7c 100644 --- a/docs-site/src/content/docs/getting-started/quick-start.md +++ b/docs-site/src/content/docs/getting-started/quick-start.md @@ -2,36 +2,103 @@ title: "Quick Start Guide" --- - -Welcome to OpenCodeHub! This guide will help you navigate the platform and start collaborating in minutes. +Welcome to OpenCodeHub! This guide will help you get started in minutes. ## 1. Login -Navigate to your instance URL (e.g., `http://localhost:3000`) and log in with the admin credentials you created during installation. - -## 2. Navigating the Dashboard -Your dashboard gives you a quick overview of: -- **Recent Repositories**: Projects you've worked on recently. -- **Your Pull Requests**: PRs requiring your attention. -- **Activity Feed**: What your team is doing. - -## 3. Create a Repository -1. Click the **+** (Plus) icon in the top right navigation bar. -2. Select **New Repository**. -3. Enter a **Repository Name** (e.g., `my-awesome-project`). -4. Choose Visibility: - - **Public**: Visible to everyone (if your instance allows it). - - **Private**: Visible only to you and collaborators. -5. Click **Create Repository**. - -## 4. Collaborate -1. **Invite Members**: Go to **Settings** > **Collaborators** to add team members. -2. **Create Issues**: Use the **Issues** tab to track tasks and bugs. -3. **Open Pull Requests**: Push a branch and open a PR to review code. ---- +Navigate to your instance URL (e.g., `http://localhost:4321`) and log in with the admin credentials you created during installation. -## Next Steps +## 2. Create a Repository + +1. Click the **+** icon in the top navigation bar +2. Select **New Repository** +3. Enter a name (e.g., `my-project`) +4. Choose visibility: **Public** or **Private** +5. Optionally initialize with a README +6. Click **Create Repository** + +## 3. Push Code + +```bash +# Clone your new repository +git clone https://git.yourcompany.com/your-username/my-project.git +cd my-project + +# Add your code +echo "# My Project" > README.md +git add . +git commit -m "Initial commit" +git push -u origin main +``` + +Or use SSH: + +```bash +git clone ssh://git@your-server:2222/your-username/my-project.git +``` + +## 4. Explore the Dashboard + +Your dashboard shows: +- **Recent Repositories** you've worked on +- **Pull Requests** requiring your attention +- **Stacked PRs** if you're using the stack workflow +- **Activity Feed** from your team + +## 5. Create a Pull Request + +1. Navigate to your repository +2. Click **Pull Requests** → **New Pull Request** +3. Select source and target branches +4. Add a title and description +5. Click **Create Pull Request** + +See [First Repository](/getting-started/first-repository/) for a complete walkthrough. -Now that you're exploring the UI, try pushing your first code: +## 6. Try Key Features + +### Stacked PRs +Break large changes into reviewable stacks: +```bash +# Install the CLI +npm install -g opencodehub-cli + +# Login +och auth login + +# Create a stacked branch +och stack create feature/part-1 + +# Make changes, commit, push +och stack submit +``` + +### AI Code Review +Enable in **Settings** → **AI Review**. Configure your preferred provider (OpenAI, Anthropic, etc.) and AI will automatically review PRs for bugs and security issues. + +### Merge Queue +Add PRs to the merge queue from the PR page. The queue ensures `main` never breaks by running CI on the merged result before pushing. + +### CI/CD Pipelines +Create `.github/workflows/*.yml` files in your repository. OpenCodeHub uses GitHub Actions-compatible syntax. + +## 7. Install the CLI + +```bash +npm install -g opencodehub-cli + +# Authenticate +och auth login --url https://git.yourcompany.com + +# Check status +och status +``` + +See [CLI Overview](/reference/cli-overview/) for all commands. + +## Next Steps -👉 **[Create Your First Repository](first-repository.md)** +- **[First Repository](/getting-started/first-repository/)** — Complete repository lifecycle +- **[Stacked PRs](/features/stacked-prs/)** — Learn the stack workflow +- **[CLI Reference](/reference/cli-commands/)** — All CLI commands +- **[Configuration](/administration/configuration/)** — Customize your instance diff --git a/docs-site/src/content/docs/guides/example.md b/docs-site/src/content/docs/guides/example.md deleted file mode 100644 index ebd0f3bc..00000000 --- a/docs-site/src/content/docs/guides/example.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Example Guide -description: A guide in my new Starlight docs site. ---- - -Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. -Writing a good guide requires thinking about what your users are trying to do. - -## Further reading - -- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework diff --git a/docs-site/src/content/docs/index.mdx b/docs-site/src/content/docs/index.mdx index 15357ccc..84f30772 100644 --- a/docs-site/src/content/docs/index.mdx +++ b/docs-site/src/content/docs/index.mdx @@ -1,9 +1,9 @@ --- title: OpenCodeHub Documentation -description: The open-source, AI-native Git server with Stacked PRs and Smart Merge Queues. +description: The open-source, self-hosted Git platform with Stacked PRs, AI Code Review, and Smart Merge Queues. hero: title: OpenCodeHub - tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, and a modern developer experience. + tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, AI code review, and a modern developer experience. --- import { Card, CardGrid } from "@astrojs/starlight/components"; @@ -11,110 +11,143 @@ import { Card, CardGrid } from "@astrojs/starlight/components"; ## Why OpenCodeHub? - - Break large features into small, dependent steps. Ship faster without the - review bottleneck. + + Break large features into small, dependent steps. Graphite-style stacked branches with automatic rebase and merge readiness checks. - - Built-in LLM agents (GPT-4/Claude) review your code for bugs and security - issues automatically. + + Built-in LLM agents (GPT-4, Claude, Gemini, Groq, Ollama, and 10+ providers) review your code for bugs and security issues automatically. - - Automate your merging strategy. Ensure `main` never breaks with speculative - CI runs. + + Stack-aware merge queue with speculative CI builds, priority lanes, and conflict detection. Ensure `main` never breaks. - - Keep your code on your servers. No "per-seat" pricing. No vendor lock-in. + + GitHub Actions-compatible workflow engine with Docker-based runners, artifact storage, and secrets management. + + + Keep your code on your servers. No per-seat pricing. No vendor lock-in. Deploy with Docker, Podman, or bare metal. + + + Built-in npm and Docker container registry. Host private packages alongside your source code. -## Architecture Overview - -![OpenCodeHub Architecture](/architecture.svg) +## Core Capabilities -OpenCodeHub is built on a modern, scalable architecture designed for performance and flexibility. + + + Full Git support with HTTP smart protocol and SSH. Repository forking, mirroring, LFS, and CODEOWNERS enforcement. + + + Full PR lifecycle with inline comments, file-level approvals, review templates, reviewer routing rules, and auto-merge. + + + Issue tracking with custom fields and statuses. Drag-and-drop project boards with columns and cards. + + + Secret scanning, vulnerability scanning, security policies, path-level permissions, and audit logging. + + + Outbound webhooks with HMAC signing. Rule-based automations for PR workflows, reviews, and deployments. + + + Multi-tenant organizations with role-based access control, team management, and fine-grained permissions. + + ## Deploy Anywhere - - The recommended way to deploy. Full Docker Compose setup with PostgreSQL and - Redis. [Deploy with Docker →](/administration/deploy-docker/) + + The recommended way to deploy. Full Docker Compose setup with PostgreSQL and Redis. + [Deploy with Docker →](/administration/deploy-docker/) - - Self-hosted PaaS alternative to Heroku/Render. Auto-deploy with built-in SSL - and managed databases. [Deploy on Coolify →](/administration/deploy-coolify/) + + Rootless containers with native systemd integration. Ideal for security-focused deployments. + [Deploy with Podman →](/administration/deploy-podman/) - - Modern hosting panel with OpenLiteSpeed. Docker or native Node.js deployment. - [Deploy on CyberPanel →](/administration/deploy-cyberpanel/) + + Deploy to Kubernetes using the bundled Helm chart with horizontal scaling. + [Deploy on Kubernetes →](/administration/kubernetes/) - - Deploy on shared hosting using Node.js or PM2. Best for budget hosting. - [Deploy on cPanel →](/administration/deploy-cpanel/) + + Self-hosted PaaS alternative to Heroku/Render. Auto-deploy with built-in SSL. + [Deploy on Coolify →](/administration/deploy-coolify/) - + Run on Synology, TrueNAS, or QNAP. Perfect for private home/team Git servers. [Deploy on NAS →](/administration/deploy-nas/) - - Rootless containers with native systemd integration. Ideal for - security-focused deployments. [Deploy with Podman - →](/administration/deploy-podman/) - - - Use Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage. + + Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage. [Deploy with Cloudflare →](/administration/deploy-cloudflare/) - - Production-ready reverse proxy config with SSL and Git-specific - optimizations. [Configure Nginx →](/administration/deploy-nginx/) + + Production-ready reverse proxy config with SSL and Git-specific optimizations. + [Configure Nginx →](/administration/deploy-nginx/) + + + Deploy on shared hosting or VPS panels using Node.js + PM2. + [cPanel →](/administration/deploy-cpanel/) | [CyberPanel →](/administration/deploy-cyberpanel/) ## Tech Stack -| Layer | Technology | -| ------------ | ------------------------------------- | -| **Frontend** | Astro, React, Tailwind CSS, shadcn/ui | -| **Backend** | Node.js, Astro API Routes, simple-git | -| **Database** | PostgreSQL, Turso (LibSQL), SQLite | -| **Storage** | Local disk, S3, R2 | -| **Auth** | JWT, 2FA, OAuth (GitHub, Google) | -| **CI/CD** | GitHub Actions compatible runners | +| Layer | Technology | +| --- | --- | +| **Framework** | Astro 4.x (SSR, Node standalone adapter) | +| **Frontend** | React 18, Tailwind CSS, Radix UI, Framer Motion | +| **Backend** | Astro API Routes (175+ endpoints), GraphQL | +| **Database** | PostgreSQL (prod), SQLite (dev), Turso/LibSQL (edge) | +| **ORM** | Drizzle ORM (38 schema tables) | +| **Auth** | JWT, bcryptjs, TOTP 2FA, OAuth (Google, GitHub), SSO/SAML, OIDC | +| **Git** | simple-git, isomorphic-git, nodegit, ssh2 | +| **Storage** | Local filesystem or S3-compatible (AWS, MinIO, R2, Garage, Wasabi) | +| **CI/CD** | GitHub Actions-compatible engine, Docker-based runners | +| **AI** | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter, Together, Bytez | +| **Queue** | BullMQ + Redis | +| **Observability** | OpenTelemetry, Prometheus metrics, Pino logging | + +## Quick Start + +```bash +# Clone and start with Docker Compose +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env # Edit secrets! +docker-compose up -d + +# Create admin user +docker-compose exec app bun run scripts/seed-admin.ts + +# Open in browser +open http://localhost:4321 +``` + +:::tip[SSH Git Access] +The SSH git server starts automatically on port 2222. Clone with: +`git clone ssh://git@localhost:2222/owner/repo.git` +::: ## Quick Links - Get OpenCodeHub running in minutes using our 1-click install script. [Read the guide - →](/getting-started/installation/) + Get OpenCodeHub running in minutes. [Read the guide →](/getting-started/installation/) - - Connect CircleCI as your external CI provider. Trigger builds and report - PR checks automatically. [Set up CircleCI →](/guides/circleci-integration/) + + All environment variables and settings. [View config →](/administration/configuration/) - Learn the `och` CLI commands for managing stacks and repositories. [View - commands →](/reference/cli-commands/) + The `och` CLI with 20+ command groups. [View commands →](/reference/cli-commands/) - Integrate with OpenCodeHub using the REST API. [Explore the API - →](/api/rest-api/) + REST API with 175+ endpoints. [Explore the API →](/api/rest-api/) + + + System design, data flow, and component overview. [View architecture →](/development/architecture/) - Help make OpenCodeHub better. Set up your local dev environment. [Start - contributing →](/development/contributing/) + Help make OpenCodeHub better. [Start contributing →](/development/contributing/) - -## Storage Options - -:::tip[Performance Recommendation] -For best performance, use **local SSD storage** via `DATA_DIR` in production. Object storage (S3/R2) introduces latency. See [Storage Adapters](/guides/storage-adapters/#️-performance-considerations) for details. -::: - -| Storage Type | Performance | Cost | Best For | -| ---------------- | ----------- | ---- | ----------------------- | -| **Local SSD** | ⚡ Fastest | $$ | Production, self-hosted | -| **S3/R2** | 🐢 Slow | $ | Backup, multi-node | diff --git a/docs-site/src/content/docs/reference/api-versioning.md b/docs-site/src/content/docs/reference/api-versioning.md new file mode 100644 index 00000000..13ba57e5 --- /dev/null +++ b/docs-site/src/content/docs/reference/api-versioning.md @@ -0,0 +1,14 @@ +# API Versioning + +OpenCodeHub provides a stable REST API and GraphQL endpoint. + +## Versioning Strategy +Currently, the REST API does not have explicit `/v1/` prefixes in the route paths. Instead, the API follows a continuous evolution model where breaking changes are heavily minimized. + +When breaking changes are unavoidable: +- They will be announced in the release notes. +- A deprecation header (`Deprecation: `) will be added to the response of the old endpoint. +- Clients will have at least one minor release cycle to migrate. + +## GraphQL +The GraphQL API inherently supports seamless evolution by deprecating specific fields using the `@deprecated` directive, allowing clients to transition without breaking version changes. diff --git a/docs-site/src/content/docs/reference/cli-auth-config.md b/docs-site/src/content/docs/reference/cli-auth-config.md index b0c31c4b..4e98d36e 100644 --- a/docs-site/src/content/docs/reference/cli-auth-config.md +++ b/docs-site/src/content/docs/reference/cli-auth-config.md @@ -10,12 +10,12 @@ This page covers how authentication and local configuration work in the OpenCode ### Login ```bash -och auth login --url http://localhost:3000 +och auth login --url http://localhost:4321 ``` Options: -- `--url `: Instance URL (defaults to `http://localhost:3000`) +- `--url `: Instance URL (defaults to `http://localhost:4321`) - `--with-token`: Prompt for a personal access token - `--token `: Non‑interactive token login diff --git a/docs-site/src/content/docs/reference/cli-core-commands.md b/docs-site/src/content/docs/reference/cli-core-commands.md index f101d2dd..f6a2f5b7 100644 --- a/docs-site/src/content/docs/reference/cli-core-commands.md +++ b/docs-site/src/content/docs/reference/cli-core-commands.md @@ -16,7 +16,7 @@ och repo list Shorthand: ```bash -och init --url http://localhost:3000 +och init --url http://localhost:4321 och push --branch main ``` diff --git a/docs-site/src/content/docs/reference/example.md b/docs-site/src/content/docs/reference/example.md deleted file mode 100644 index 0224f096..00000000 --- a/docs-site/src/content/docs/reference/example.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Example Reference -description: A reference page in my new Starlight docs site. ---- - -Reference pages are ideal for outlining how things work in terse and clear terms. -Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what you're documenting. - -## Further reading - -- Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework diff --git a/docs-site/src/content/docs/tutorials/zero-to-production.md b/docs-site/src/content/docs/tutorials/zero-to-production.md index ef6291ca..253809d0 100644 --- a/docs-site/src/content/docs/tutorials/zero-to-production.md +++ b/docs-site/src/content/docs/tutorials/zero-to-production.md @@ -34,7 +34,7 @@ bun run scripts/seed-admin.ts npm run dev ``` -Open the app at `http://localhost:3000` and log in. +Open the app at `http://localhost:4321` and log in. ## 5) Production deployment diff --git a/docs-site/src/custom.css b/docs-site/src/custom.css index 3f810eeb..5d2d4d4c 100644 --- a/docs-site/src/custom.css +++ b/docs-site/src/custom.css @@ -11,10 +11,10 @@ } :root { - --och-accent-1: #2dd4bf; - --och-accent-2: #8be9fd; - --och-accent-3: #bd93f9; - --och-accent-4: #ff79c6; + --och-accent-1: #0d9488; + --och-accent-2: #38bdf8; + --och-accent-3: #a78bfa; + --och-accent-4: #f472b6; --och-hero-surface: color-mix(in srgb, var(--sl-color-bg) 92%, transparent); } diff --git a/docs/FREE-DEPLOYMENT.md b/docs/FREE-DEPLOYMENT.md new file mode 100644 index 00000000..87776872 --- /dev/null +++ b/docs/FREE-DEPLOYMENT.md @@ -0,0 +1,395 @@ +# OpenCodeHub — Free Deployment Options + +> Deploy OpenCodeHub for free with persistent file storage for git repositories. + +--- + +## Quick Comparison + +| Platform | Cost | Storage | RAM | CPU | Best For | +|----------|------|---------|-----|-----|----------| +| **Oracle Cloud Free** | $0 forever | 200GB | 24GB | 4 ARM cores | Best overall | +| **Home Server/NAS** | $0 | Unlimited | Your hardware | Your hardware | Privacy-focused | +| **Fly.io** | $0 (limited) | 3GB free | 256MB | Shared | Quick testing | +| **Google Cloud Free** | $0 forever | 30GB | 1GB | 0.25 vCPU | Light usage | +| **Railway** | $5 trial | 1GB | 512MB | Shared | Quick demo | +| **Hetzner** | ~€4/mo | 40GB | 4GB | 2 vCPU | Best value paid | + +--- + +## Option 1: Oracle Cloud Free Tier (Recommended) + +**Always Free** — No credit card charge, no time limit. + +### What You Get +- 4 ARM cores (Ampere A1) +- 24 GB RAM +- 200 GB boot volume +- 2 x 1TB block volumes (total 2TB) +- Free PostgreSQL database +- Free Redis + +### Steps + +```bash +# 1. Sign up at cloud.oracle.com +# 2. Create a VM instance: +# - Name: opencodehub +# - Image: Ubuntu 22.04 +# - Shape: VM.Standard.A1.Flex (4 OCPU, 24 GB RAM) +# - Upload SSH key +# - Create boot volume: 100 GB + +# 3. SSH into your instance +ssh -i ~/.ssh/key ubuntu@YOUR_PUBLIC_IP + +# 4. Install Docker +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +sudo usermod -aG docker $USER + +# 5. Install Docker Compose +sudo apt install docker-compose-plugin -y + +# 6. Clone OpenCodeHub +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub + +# 7. Configure +cp .env.example .env +nano .env # Edit secrets + +# 8. Start +sudo docker compose up -d + +# 9. Create admin +sudo docker compose exec app bun run scripts/seed-admin.ts +``` + +### Access Your Instance +``` +Web: http://YOUR_PUBLIC_IP:4321 +SSH: ssh://git@YOUR_PUBLIC_IP:2222/owner/repo.git +HTTPS: Set up Nginx + Let's Encrypt (free) +``` + +--- + +## Option 2: Home Server / NAS + +**$0 forever** — Use hardware you already own. + +### What You Need +- Any computer running Linux/Mac/Windows +- 2GB+ RAM recommended +- 20GB+ free disk space + +### Quick Start (Linux/Mac) + +```bash +# Install Docker +curl -fsSL https://get.docker.com | sh + +# Clone and start +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env +nano .env # Set secrets +docker compose up -d + +# Create admin +docker compose exec app bun run scripts/seed-admin.ts +``` + +### NAS Deployment + +**Synology / TrueNAS / QNAP:** +1. Install Docker from Package Center +2. Use Docker Compose or Portainer +3. Mount a shared folder for `/data` + +### Access from Anywhere +- **Tailscale** (free): `tailscale up` → access via Tailscale IP +- **Cloudflare Tunnel** (free): Expose without opening ports +- **Port forwarding**: Open port 4321 on your router + +--- + +## Option 3: Fly.io Free Tier + +**Free** — 3GB persistent volume included. + +### Steps + +```bash +# 1. Install flyctl +curl -L https://fly.io/install.sh | sh + +# 2. Login +fly auth login + +# 3. Launch +fly launch --name opencodehub + +# 4. Add persistent volume +fly volume create opencodehub_data --region sjc --size 3 + +# 5. Set secrets +fly secrets set JWT_SECRET=$(openssl rand -hex 32) +fly secrets set SESSION_SECRET=$(openssl rand -hex 32) +fly secrets set DATABASE_URL="postgresql://..." +fly secrets set REDIS_URL="redis://..." + +# 6. Deploy +fly deploy +``` + +### Limitations +- 3GB storage (enough for small repos) +- 256MB RAM (may be slow for large repos) +- Apps休眠 after inactivity (cold starts take ~30s) + +--- + +## Option 4: Google Cloud Free Tier + +**Always Free** — e2-micro instance. + +### Steps + +```bash +# 1. Sign up at cloud.google.com +# 2. Create e2-micro VM: +# - Machine type: e2-micro (0.25 vCPU, 1GB RAM) +# - Boot disk: 30GB SSD (free) +# - Firewall: Allow HTTP (80), SSH (22) + +# 3. SSH in and install Docker +ssh -i ~/.ssh/key USER@EXTERNAL_IP +sudo apt update && sudo apt install docker.io docker-compose-plugin -y + +# 4. Deploy OpenCodeHub +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env +nano .env +docker compose up -d +``` + +### Limitations +- Only 1GB RAM (tight for production) +- 0.25 vCPU (slow builds) +- Good for personal/light use + +--- + +## Option 5: Self-Hosted on Existing VPS + +If you already have a VPS (Hetzner, DigitalOcean, Linode, etc.): + +```bash +# SSH into your server +ssh root@YOUR_SERVER_IP + +# One-line install +curl -fsSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash +``` + +--- + +## Option 6: Docker on Windows/Mac + +For local development or personal use: + +```bash +# Install Docker Desktop for Windows/Mac +# https://docs.docker.com/get-docker/ + +# Clone and start +git clone https://github.com/swadhinbiswas/OpencodeHub.git +cd OpencodeHub +cp .env.example .env +nano .env +docker compose up -d + +# Create admin +docker compose exec app bun run scripts/seed-admin.ts +``` + +--- + +## Storage Configuration + +### Local Storage (Default) +```bash +STORAGE_TYPE=local +STORAGE_PATH=/data/storage +GIT_REPOS_PATH=/data/repositories +``` + +### S3-Compatible (MinIO on same server) +```bash +# Add to docker-compose.yml: +minio: + image: minio/minio + command: server /data --console-address ":9001" + volumes: + - ./data/minio:/data + ports: + - "9001:9001" + +STORAGE_TYPE=s3 +STORAGE_ENDPOINT=http://minio:9000 +STORAGE_BUCKET=opencodehub +STORAGE_ACCESS_KEY_ID=minioadmin +STORAGE_SECRET_ACCESS_KEY=minioadmin +``` + +--- + +## HTTPS Setup (Free) + +### With Let's Encrypt + Nginx + +```bash +# Install Nginx and Certbot +sudo apt install nginx certbot python3-certbot-nginx -y + +# Create Nginx config +sudo nano /etc/nginx/sites-available/opencodehub +``` + +```nginx +server { + listen 80; + server_name git.yourdomain.com; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl http2; + server_name git.yourdomain.com; + + ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; + + client_max_body_size 500M; + + location / { + proxy_pass http://localhost:4321; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # WebSocket support + location /api/realtime { + proxy_pass http://localhost:4321; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +```bash +# Enable and get SSL +sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/ +sudo certbot --nginx -d git.yourdomain.com +``` + +--- + +## Environment Variables (Production) + +```bash +# Generate all secrets +JWT_SECRET=$(openssl rand -hex 32) +SESSION_SECRET=$(openssl rand -hex 32) +INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) +CRON_SECRET=$(openssl rand -hex 32) +RUNNER_SECRET=$(openssl rand -hex 32) +AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) +WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) + +# Your .env file +DATABASE_DRIVER=postgres +DATABASE_URL=postgresql://opencodehub:PASSWORD@localhost:5432/opencodehub +DATABASE_SSL=false +REDIS_URL=redis://localhost:6379 +SITE_URL=https://git.yourdomain.com +STORAGE_TYPE=local +GIT_REPOS_PATH=/data/repositories +NODE_ENV=production +``` + +--- + +## Backup Strategy + +### Automated Backup Script +```bash +#!/bin/bash +# backup.sh — Run daily via cron +BACKUP_DIR="/backups/$(date +%Y-%m-%d)" +mkdir -p "$BACKUP_DIR" + +# Database dump +docker compose exec -T postgres pg_dump -U opencodehub opencodehub > "$BACKUP_DIR/db.sql" + +# Git repos +tar czf "$BACKUP_DIR/repos.tar.gz" ./data/repositories/ + +# Upload to S3 (optional) +# aws s3 sync "$BACKUP_DIR" s3://your-backup-bucket/opencodehub/ +``` + +### Cron Job +```bash +# Add to crontab +crontab -e +0 2 * * * /path/to/backup.sh +``` + +--- + +## Monitoring + +### Health Check +```bash +# Check if app is running +curl -f http://localhost:4321/api/health || echo "DOWN" + +# Check Docker status +docker compose ps +docker compose logs --tail=50 app +``` + +### Uptime Robot (Free) +1. Sign up at uptimerobot.com +2. Add HTTP monitor: `http://YOUR_IP:4321/api/health` +3. Get notified if your instance goes down + +--- + +## Recommended Free Setup + +For a **personal Git server** that stores all your code: + +| Component | Choice | +|-----------|--------| +| **Cloud** | Oracle Cloud Free Tier | +| **Server** | ARM A1 (4 cores, 24GB RAM) | +| **Storage** | 100GB boot + 1TB block volume | +| **Database** | PostgreSQL (Docker) | +| **Cache** | Redis (Docker) | +| **HTTPS** | Nginx + Let's Encrypt | +| **Access** | Tailscale (private) or port forwarding | +| **Backups** | Cron script + local S3 | + +**Total cost: $0/month forever.** + +--- + +*Generated for OpenCodeHub free deployment.* diff --git a/docs/RENDER-DEPLOYMENT.md b/docs/RENDER-DEPLOYMENT.md new file mode 100644 index 00000000..32ead58d --- /dev/null +++ b/docs/RENDER-DEPLOYMENT.md @@ -0,0 +1,163 @@ +# OpenCodeHub — Render Deployment Guide + +> Deploy to Render (Singapore) with free PostgreSQL + Upstash Redis. $0/month. + +--- + +## What You Get + +- OpenCodeHub running in Asia (Singapore) +- Free PostgreSQL database +- Free Redis via Upstash +- 1GB persistent storage for git repos +- HTTPS included +- Admin account + +--- + +## Step 1: Create Upstash Redis (2 min) + +1. Go to **https://upstash.com** → Sign up (free) +2. Click **Create Database** +3. Settings: + - **Name**: `opencodehub` + - **Region**: **Asia Pacific (Singapore)** + - **Plan**: Pay as you go (free tier: 10K commands/day) +4. Click **Create** +5. Copy the **Redis URL** (looks like `rediss://...@...`) +6. Keep this tab open — you'll need it in Step 3 + +--- + +## Step 2: Deploy to Render (5 min) + +1. Go to **https://render.com** → Sign up (free) +2. Click **New** → **Blueprint** +3. Connect your GitHub account +4. Select your OpenCodeHub repository +5. Render detects `render.yaml` and shows: + - `opencodehub-db` (PostgreSQL — Singapore) + - `opencodehub` (Web Service — Singapore) +6. Click **Apply** +7. Wait for deployment (~3-5 min first time) + +--- + +## Step 3: Set Redis URL (1 min) + +1. Go to your Web Service → **Environment** tab +2. Find `REDIS_URL` (shows "Not set") +3. Click the pencil icon → Paste your **Upstash Redis URL** +4. Click **Save** + +--- + +## Step 4: Set Site URL (1 min) + +1. In the same **Environment** tab +2. Find `SITE_URL` (shows "Not set") +3. Click the pencil icon → Enter your Render URL: + ``` + https://opencodehub.onrender.com + ``` + (Replace with your actual URL from the service overview) +4. Click **Save** + +--- + +## Step 5: Manual Deploy + +1. Go to your Web Service → **Manual Deploy** +2. Click **Deploy latest commit** +3. Wait for build to complete (~2-3 min) +4. Click the **URL** at the top to open your instance + +--- + +## Step 6: Create Admin Account + +You'll see the **Setup Wizard**. Fill in: + +| Field | Value | +|-------|-------| +| Username | `admin` (or your choice) | +| Email | Your email | +| Password | Strong password | + +Click **Create Account** — you're now logged in as admin! + +--- + +## Step 7: Test It + +```bash +# Create a repo via web UI, then clone: +git clone https://opencodehub.onrender.com/your-username/my-repo.git + +# Push code: +cd my-repo +echo "# Hello" > README.md +git add . && git commit -m "Initial" +git push origin main +``` + +--- + +## Done! Summary + +| Component | Service | Region | Cost | +|-----------|---------|--------|------| +| App | Render Free | Singapore | $0 | +| PostgreSQL | Render Free | Singapore | $0 | +| Redis | Upstash Free | Singapore | $0 | +| Storage | Render Disk 1GB | Singapore | $0 | +| **Total** | | | **$0/month** | + +--- + +## Keep Alive (Optional) + +Render free tier spins down after 15min inactivity. To prevent: + +1. Go to **https://uptimerobot.com** → Sign up (free) +2. Add Monitor → Type: HTTP(s) +3. URL: `https://opencodehub.onrender.com/api/health` +4. Monitoring interval: 5 minutes +5. Save — your app stays awake + +--- + +## Upgrade Path (When You Grow) + +| Need | Upgrade | Cost | +|------|---------|------| +| Always-on (no spin-down) | Render Starter | $7/mo | +| More storage (10GB+) | Render Disk upgrade | $1-7/mo | +| More Redis commands | Upstash Pay-as-you-go | ~$0.20/10K | +| PostgreSQL expiry | Render Starter DB | $7/mo | + +--- + +## Troubleshooting + +**App won't start?** +- Check **Logs** tab in Render +- Verify `REDIS_URL` is set correctly +- Ensure `SITE_URL` matches your Render URL + +**Redis connection failed?** +- Verify Redis URL from Upstash (starts with `rediss://`) +- Check Upstash dashboard shows "Active" +- Ensure region is Singapore + +**Git clone fails?** +- Use HTTPS: `git clone https://your-url.onrender.com/user/repo.git` +- SSH not available on Render free tier (use HTTPS) + +**Slow first request?** +- Normal for free tier (cold start ~30s) +- Add UptimeRobot ping to keep it warm + +--- + +*OpenCodeHub — Deployed on Render, Asia Region, $0/month.* diff --git a/docs/administration/air-gapped.md b/docs/administration/air-gapped.md new file mode 100644 index 00000000..0ad4dc59 --- /dev/null +++ b/docs/administration/air-gapped.md @@ -0,0 +1,33 @@ +# Air-gapped Environments + +OpenCodeHub can be deployed in fully offline, air-gapped networks. + +## Prerequisites +- A local container registry to host the OpenCodeHub Docker image. +- A local NPM/mirror registry if you plan to build from source. + +## Deployment Steps +1. On a machine with internet access, pull the required images: + ```bash + docker pull ghcr.io/swadhinbiswas/opencodehub:latest + docker pull postgres:15-alpine + docker pull redis:7-alpine + ``` +2. Save the images to tar archives: + ```bash + docker save ghcr.io/swadhinbiswas/opencodehub:latest -o och.tar + docker save postgres:15-alpine -o pg.tar + docker save redis:7-alpine -o redis.tar + ``` +3. Transfer the archives to the air-gapped network via secure media. +4. Load the images on the target machine: + ```bash + docker load -i och.tar + docker load -i pg.tar + docker load -i redis.tar + ``` + +## Configuration Adjustments +- Disable external OAuth providers (GitHub, Google) and rely exclusively on local authentication, LDAP, or an internal SAML provider. +- Disable outbound webhooks pointing to external SaaS platforms. +- Ensure the AI Code Review module is configured to use a `local` provider (e.g., an internal Ollama instance) rather than cloud APIs like OpenAI. diff --git a/docs/administration/backup-restore.md b/docs/administration/backup-restore.md new file mode 100644 index 00000000..2522445b --- /dev/null +++ b/docs/administration/backup-restore.md @@ -0,0 +1,29 @@ +# Backup and Restore + +This guide covers how to back up and restore an OpenCodeHub instance. + +## Backup Strategy + +### 1. Database +For PostgreSQL, use `pg_dump`: +```bash +pg_dump -U opencodehub -d opencodehub_db > opencodehub_backup.sql +``` + +### 2. Git Repositories +The Git repositories are stored in the path defined by `GIT_REPOS_PATH` (default: `./data/repos`). +You can back this up using `rsync` or `tar`: +```bash +tar -czvf repos_backup.tar.gz ./data/repos/ +``` + +### 3. Object Storage +If you are using S3 or another object storage provider for artifacts, ensure you have enabled versioning and standard bucket backups through your cloud provider. If using local storage, back up the configured `./data/storage` directory. + +## Restore Strategy +To restore: +1. Stop the application and worker processes. +2. Restore the database using `psql`. +3. Extract the Git repositories to `GIT_REPOS_PATH`. +4. Restore object storage data. +5. Restart the application. diff --git a/docs/administration/configuration.md b/docs/administration/configuration.md index 8c977653..60210f08 100644 --- a/docs/administration/configuration.md +++ b/docs/administration/configuration.md @@ -6,8 +6,8 @@ OpenCodeHub is configured exclusively via environment variables. | Variable | Description | Required | Default | |----------|-------------|----------|---------| -| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:3000` | -| `PORT` | Port to listen on | No | `3000` | +| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:4321` | +| `PORT` | Port to listen on | No | `4321` | | `NODE_ENV` | `development` or `production` | No | `development` | ## Security (Critical) @@ -17,6 +17,11 @@ OpenCodeHub is configured exclusively via environment variables. | `JWT_SECRET` | Signs auth tokens | `openssl rand -hex 32` | | `SESSION_SECRET` | Signs session cookies | `openssl rand -hex 32` | | `INTERNAL_HOOK_SECRET` | Secures git hooks | `openssl rand -hex 32` | +| `CRON_SECRET` | Secures cron endpoints | `openssl rand -hex 32` | +| `RUNNER_SECRET` | Shared secret between server and CI runners | `openssl rand -hex 32` | +| `AI_CONFIG_ENCRYPTION_KEY` | Encrypts AI provider keys at rest | `openssl rand -hex 32` | +| `WORKFLOW_SECRET_ENCRYPTION_KEY` | Encrypts workflow/repo secrets at rest | `openssl rand -hex 32` | +| `METRICS_TOKEN` | Bearer token for `GET /api/metrics` (Prometheus). Empty = public | `openssl rand -hex 32` | ## Database diff --git a/docs/administration/deploy-nas.md b/docs/administration/deploy-nas.md index 8a4375d7..a9581224 100644 --- a/docs/administration/deploy-nas.md +++ b/docs/administration/deploy-nas.md @@ -4,6 +4,23 @@ Deploy OpenCodeHub on your **Network Attached Storage (NAS)** device. This is perfect for small teams, personal use, or as a private Git server at home. Works on Synology DSM, TrueNAS SCALE, QNAP QTS, and any NAS supporting Docker. +> **Recommended:** the repository ships `docker-compose.nas.yml`, which is +> the production-safe version of every snippet below. It runs the web app, +> the **SSH Git server**, and the **background worker** as separate +> containers, requires secrets instead of defaulting them, and binds every +> data directory to one host path (`OCH_DATA_DIR`, default `/mnt/och`) — +> perfect for a dedicated hard disk or NAS share: +> +> ```bash +> cp .env.example .env # then fill in ALL secrets +> OCH_DATA_DIR=/volume1/opencodehub docker compose -f docker-compose.nas.yml up -d +> ``` +> +> The inline YAML in this guide is a minimal reference. Note that the **web +> app container does not run SSH Git** — if you use a snippet as-is, port +> 2222 will not serve SSH. Add the `ssh-git` service (or use +> `docker-compose.nas.yml`) for SSH access. + --- ## Prerequisites @@ -135,7 +152,6 @@ docker run -d \ --link opencodehub-postgres:postgres \ --link opencodehub-redis:redis \ -p 4321:4321 \ - -p 2222:2222 \ -e NODE_ENV=production \ -e DATABASE_URL=postgresql://opencodehub:POSTGRES_PASS@host.docker.internal:5432/opencodehub \ -e REDIS_URL=redis://:REDIS_PASS@host.docker.internal:6379 \ @@ -145,19 +161,24 @@ docker run -d \ -e CRON_SECRET=$(openssl rand -hex 32) \ -e RUNNER_SECRET=$(openssl rand -hex 32) \ -e WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) \ + -e AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) \ -e SITE_URL=https://git.yourddns.net \ -e STORAGE_TYPE=local \ -e STORAGE_PATH=/data/storage \ - -e REPOS_PATH=/data/repos \ - -e SSH_HOST_KEY_PATH=/data/ssh/host_key \ + -e GIT_REPOS_PATH=/data/repos \ + -e GIT_SSH_HOST_KEY=/data/ssh/host_key \ -v /volume1/opencodehub/repos:/data/repos \ -v /volume1/opencodehub/storage:/data/storage \ -v /volume1/opencodehub/ssh:/data/ssh \ -v /volume1/opencodehub/cache:/data/cache \ - swadhinbiswas/opencodehub:latest + opencodehub/opencodehub:latest ``` > **Note:** On Synology, `host.docker.internal` may not work. Use your NAS IP address instead (e.g., `192.168.1.100`). +> +> **SSH Git:** the app container serves the web UI only. For SSH access, +> run a second container from the same image with `PROCESS_TYPE=ssh` and +> expose port 2222 (this is exactly what `docker-compose.nas.yml` does). #### Option B: Build from Source @@ -175,9 +196,9 @@ services: restart: unless-stopped ports: - "4321:4321" - - "2222:2222" environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub - REDIS_URL=redis://:YOUR_PASS@redis:6379 - JWT_SECRET=YOUR_SECRET @@ -186,11 +207,72 @@ services: - CRON_SECRET=YOUR_SECRET - RUNNER_SECRET=YOUR_SECRET - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET - SITE_URL=https://git.yourddns.net - STORAGE_TYPE=local - STORAGE_PATH=/data/storage + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /volume1/opencodehub/repos:/data/repos + - /volume1/opencodehub/storage:/data/storage + - /volume1/opencodehub/ssh:/data/ssh + - /volume1/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + ssh-git: + build: + context: /volume1/docker/OpenCodeHub + dockerfile: Dockerfile + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "2222:2222" + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /volume1/opencodehub/repos:/data/repos + - /volume1/opencodehub/storage:/data/storage + - /volume1/opencodehub/ssh:/data/ssh + - /volume1/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + worker: + build: + context: /volume1/docker/OpenCodeHub + dockerfile: Dockerfile + container_name: opencodehub-worker + restart: unless-stopped + environment: + - NODE_ENV=production + - PROCESS_TYPE=worker + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos - REPOS_PATH=/data/repos - - SSH_HOST_KEY_PATH=/data/ssh/host_key + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage volumes: - /volume1/opencodehub/repos:/data/repos - /volume1/opencodehub/storage:/data/storage @@ -301,14 +383,14 @@ version: '3.8' services: app: - image: swadhinbiswas/opencodehub:latest + image: opencodehub/opencodehub:latest container_name: opencodehub restart: unless-stopped ports: - "4321:4321" - - "2222:2222" environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub - REDIS_URL=redis://:YOUR_PASS@redis:6379 - JWT_SECRET=YOUR_SECRET @@ -317,11 +399,68 @@ services: - CRON_SECRET=YOUR_SECRET - RUNNER_SECRET=YOUR_SECRET - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET - SITE_URL=https://git.yourdomain.com - STORAGE_TYPE=local - STORAGE_PATH=/data/storage + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /mnt/YOURPOOL/opencodehub/repos:/data/repos + - /mnt/YOURPOOL/opencodehub/storage:/data/storage + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh + - /mnt/YOURPOOL/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + ssh-git: + image: opencodehub/opencodehub:latest + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "2222:2222" + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - /mnt/YOURPOOL/opencodehub/repos:/data/repos + - /mnt/YOURPOOL/opencodehub/storage:/data/storage + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh + - /mnt/YOURPOOL/opencodehub/cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + worker: + image: opencodehub/opencodehub:latest + container_name: opencodehub-worker + restart: unless-stopped + environment: + - NODE_ENV=production + - PROCESS_TYPE=worker + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos - REPOS_PATH=/data/repos - - SSH_HOST_KEY_PATH=/data/ssh/host_key + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage volumes: - /mnt/YOURPOOL/opencodehub/repos:/data/repos - /mnt/YOURPOOL/opencodehub/storage:/data/storage @@ -443,7 +582,7 @@ Install Nginx Proxy Manager from the TrueNAS Apps catalog: #### Create OpenCodeHub: -1. Search and install `swadhinbiswas/opencodehub:latest` +1. Search and install `opencodehub/opencodehub:latest` 2. Name: `opencodehub` 3. Network: `opencodehub-network` 4. Environment variables (same as Synology example) @@ -496,14 +635,14 @@ version: '3.8' services: app: - image: swadhinbiswas/opencodehub:latest + image: opencodehub/opencodehub:latest container_name: opencodehub restart: unless-stopped ports: - "4321:4321" - - "2222:2222" environment: - NODE_ENV=production + - PROCESS_TYPE=app - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub - REDIS_URL=redis://:YOUR_PASS@redis:6379 - JWT_SECRET=YOUR_SECRET @@ -512,11 +651,68 @@ services: - CRON_SECRET=YOUR_SECRET - RUNNER_SECRET=YOUR_SECRET - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET - SITE_URL=https://git.yourdomain.com - STORAGE_TYPE=local - STORAGE_PATH=/data/storage + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - ./repos:/data/repos + - ./storage:/data/storage + - ./ssh:/data/ssh + - ./cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + ssh-git: + image: opencodehub/opencodehub:latest + container_name: opencodehub-ssh + restart: unless-stopped + ports: + - "2222:2222" + environment: + - NODE_ENV=production + - PROCESS_TYPE=ssh + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos + - REPOS_PATH=/data/repos + - GIT_SSH_HOST_KEY=/data/ssh/host_key + - GIT_SSH_PORT=2222 + volumes: + - ./repos:/data/repos + - ./storage:/data/storage + - ./ssh:/data/ssh + - ./cache:/data/cache + depends_on: + - postgres + - redis + networks: + - och-net + + worker: + image: opencodehub/opencodehub:latest + container_name: opencodehub-worker + restart: unless-stopped + environment: + - NODE_ENV=production + - PROCESS_TYPE=worker + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub + - REDIS_URL=redis://:YOUR_PASS@redis:6379 + - JWT_SECRET=YOUR_SECRET + - INTERNAL_HOOK_SECRET=YOUR_SECRET + - GIT_REPOS_PATH=/data/repos - REPOS_PATH=/data/repos - - SSH_HOST_KEY_PATH=/data/ssh/host_key + - STORAGE_TYPE=local + - STORAGE_PATH=/data/storage volumes: - ./repos:/data/repos - ./storage:/data/storage diff --git a/docs/administration/deployment.md b/docs/administration/deployment.md index e9e80875..f66a5cab 100644 --- a/docs/administration/deployment.md +++ b/docs/administration/deployment.md @@ -116,9 +116,6 @@ STORAGE_SECRET_ACCESS_KEY= **Optional but Recommended:** ```bash -# Error Monitoring -SENTRY_DSN=https://your-sentry-dsn - # Email SMTP_HOST=smtp.example.com SMTP_PORT=587 @@ -127,7 +124,7 @@ SMTP_PASSWORD= SMTP_FROM=noreply@yourcompany.com # Redis (for session management) -REDIS_URL=redis://localhost:6379 +REDIS_URL=redis://:your-password@localhost:6379 ``` ### Deployment Steps @@ -138,21 +135,19 @@ git clone https://github.com/swadhinbiswas/OpencodeHub.git cd OpenCodeHub # 2. Install dependencies -npm install # or bun install +bun install # or npm install # 3. Create production .env cp .env.example .env -nano .env # Configure all production values +nano .env # Configure all production values (secrets: openssl rand -hex 32) # 4. Validate environment bun run src/lib/env-validation.ts # Should output: ✅ Environment validation passed -# 5. Run database migrations -npm run db:push -# or generate versioned migrations: -npm run db:generate -npm run db:migrate +# 5. Run database migrations (applies committed drizzle/ migrations) +bun run migrate +# or for dev: npm run db:push # 6. Create admin user bun run scripts/seed-admin.ts @@ -161,59 +156,68 @@ bun run scripts/seed-admin.ts # 7. Build for production npm run build -# 8. Start production server -npm start +# 8. Start the production server (built in step 7) +npm start # → node ./dist/server/entry.mjs ``` +> **Docker** is the recommended path for production. Use the repository's +> `docker-compose.yml` (quickstart) or `docker-compose.production.yml` +> (Caddy TLS + isolated networks) — do not hand-roll a compose file. + ### Docker Deployment (Recommended) -```yaml -# docker-compose.prod.yml -version: '3.8' +Use the repository's own compose files — they are kept in sync with the +codebase and are production-safe (no default secrets, migrations applied +at boot, worker + SSH services included): + +```bash +# Quickstart (app + SSH + worker + PostgreSQL + Redis) +cp .env.example .env # then set ALL secrets +docker compose up -d + +# Production profile (adds Caddy TLS termination + isolated networks) +docker compose -f docker-compose.production.yml --profile production up -d + +# NAS / dedicated-disk deployments +OCH_DATA_DIR=/mnt/nas/opencodehub docker compose -f docker-compose.nas.yml up -d +``` + +Minimal reference compose (for illustration only): +```yaml services: app: build: . restart: always ports: - - "3000:3000" - env_file: .env.production + - "4321:4321" + env_file: .env + environment: + NODE_ENV: production depends_on: - postgres - redis - volumes: - - ./data:/app/data - environment: - NODE_ENV: production - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] - interval: 30s - timeout: 10s - retries: 3 postgres: - image: postgres:15-alpine + image: postgres:16-alpine restart: always environment: POSTGRES_DB: opencodehub POSTGRES_USER: opencodehub POSTGRES_PASSWORD: ${DATABASE_PASSWORD} - volumes: - - postgres_data:/var/lib/postgresql/data - ports: - - "5432:5432" redis: image: redis:7-alpine restart: always command: redis-server --requirepass ${REDIS_PASSWORD} - ports: - - "6379:6379" volumes: postgres_data: ``` +> Exposed ports: **4321** (web/API/Git HTTP) and **2222** (SSH Git). +> PostgreSQL (5432) and Redis (6379) stay on an internal network. + ```bash # Deploy with Docker docker-compose -f docker-compose.prod.yml up -d @@ -244,7 +248,7 @@ server { # Proxy configuration location / { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -258,7 +262,7 @@ server { # Git operations (larger timeouts) location ~ ^/[^/]+/[^/]+\.git/ { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_read_timeout 600s; proxy_send_timeout 600s; proxy_buffer_size 128k; diff --git a/docs/administration/expose-nas.md b/docs/administration/expose-nas.md index 9b69bd71..e7dcc76f 100644 --- a/docs/administration/expose-nas.md +++ b/docs/administration/expose-nas.md @@ -43,7 +43,7 @@ firewall never sees an inbound connection to OpenCodeHub ports. Whatever exposure method you pick, you need: -1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `3000`). +1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `4321`). 2. **A reachable hostname** (Tailscale or Cloudflare) pointed at the tunnel. 3. **A persistent `cloudflared` or `tailscaled`** process — running on the NAS, in Docker, or as a system service. 4. **Trusted Proxies** correctly set in OpenCodeHub (so the real client IP is preserved and rate limits are not bypassed). See `TRUSTED_PROXIES` in `.env.example`. @@ -105,11 +105,11 @@ tailscale ip -4 # 100.x.y.z # in the Tailscale admin console: https://login.tailscale.com/admin # (turn on the "Funnel" feature flag). # -# Then on the NAS, expose the port OpenCodeHub listens on (default 3000): -sudo tailscale funnel 3000 on +# Then on the NAS, expose the port OpenCodeHub listens on (default 4321): +sudo tailscale funnel 4321 on # Or expose a specific path with HTTPS-only: -sudo tailscale funnel --https=443 http://localhost:3000 +sudo tailscale funnel --https=443 http://localhost:4321 ``` You will get a public URL like `https://nas-hostname.tail-net.ts.net`. @@ -137,7 +137,7 @@ For Synology DSM, use **Task Scheduler → Triggered task → Boot-up** and run: ```bash -sudo tailscale funnel 3000 on +sudo tailscale funnel 4321 on ``` For TrueNAS SCALE, configure the Tailscale app's "Post-Start" command @@ -267,11 +267,11 @@ In the Cloudflare dashboard → your tunnel → **Public Hostname**: |---|---| | Subdomain | `git` (or anything) | | Domain | `example.com` | -| **Service** | Type: `HTTP`, URL: `opencodehub:3000` (or `localhost:3000` if `cloudflared` is host-networking) | +| **Service** | Type: `HTTP`, URL: `opencodehub:4321` (or `localhost:4321` if `cloudflared` is host-networking) | If you used Option A (Docker bridge network), `opencodehub` must be the service name in your OpenCodeHub `docker-compose.yml`. If you used -host networking, use `localhost:3000`. +host networking, use `localhost:4321`. Save. After a few seconds, `https://git.example.com` should reach your OpenCodeHub instance. @@ -383,8 +383,8 @@ on the NAS itself. - Cloudflare: the tunnel status is **HEALTHY** in the Zero Trust dashboard. - Confirm OpenCodeHub is reachable from the tunnel's network namespace: - - Tailscale (host networking): `curl http://localhost:3000` - - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:3000/` + - Tailscale (host networking): `curl http://localhost:4321` + - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:4321/` ### Real client IP is wrong diff --git a/docs/administration/horizontal-scaling.md b/docs/administration/horizontal-scaling.md new file mode 100644 index 00000000..8df9dbe4 --- /dev/null +++ b/docs/administration/horizontal-scaling.md @@ -0,0 +1,17 @@ +# Horizontal Scaling + +OpenCodeHub is designed to be scaled horizontally to handle high traffic and large workloads. + +## Stateless Application Servers +The main Astro application and REST API endpoints are mostly stateless. You can run multiple instances of the app behind a load balancer (e.g., Nginx, HAProxy, or a cloud LB). + +## State Management +- **Database**: Ensure your database (PostgreSQL) is appropriately scaled or managed (e.g., AWS RDS). +- **Redis**: Redis is strictly required for horizontal scaling. It handles sessions, rate limiting, distributed locking, and BullMQ queues for the background workers. +- **Background Workers**: You can spin up multiple worker processes (`npm run worker:start`) across different nodes. They will pull jobs from the shared Redis queue. + +## Shared Git Storage +To allow multiple app instances to access Git repositories over SSH and HTTP, the `GIT_REPOS_PATH` must point to a shared network file system (e.g., NFS, EFS) that supports POSIX file locking. + +## Pluggable Blob Storage +Configure `STORAGE_TYPE=s3` and use an S3-compatible backend (MinIO, AWS S3, Cloudflare R2) so all nodes share access to the same artifact/LFS storage. diff --git a/docs/administration/incident-runbook.md b/docs/administration/incident-runbook.md index 365702b9..a732a244 100644 --- a/docs/administration/incident-runbook.md +++ b/docs/administration/incident-runbook.md @@ -48,7 +48,7 @@ curl -s https://your-domain.com/api/metrics | grep active_runners kubectl set env deployment/opencodehub FEATURE_X_DISABLED=true # Option B: Rollback (Docker) -docker run --rm swadhinbiswas/opencodehub:previous-tag +docker run --rm opencodehub/opencodehub:previous-tag # Update docker-compose.yml with previous tag docker compose -f docker-compose.production.yml up -d diff --git a/docs/administration/known-accepted-vulns.json b/docs/administration/known-accepted-vulns.json index 0ea60a7b..0666034a 100644 --- a/docs/administration/known-accepted-vulns.json +++ b/docs/administration/known-accepted-vulns.json @@ -1,8 +1,23 @@ { "_comment": "Vulnerabilities that are allowed past CI gates. Each entry MUST be reviewed and documented in docs/administration/security.md with a clear remediation plan and target date. Do NOT add new entries without justification.", "accepted": [ + "@astrojs/vercel", + "@opentelemetry/propagator-jaeger", + "@opentelemetry/sdk-node", + "@vercel/nft", "astro", + "brace-expansion", + "cacache", + "gaxios", + "gcp-metadata", + "glob", + "make-fetch-happen", + "minimatch", + "node-gyp", + "nodegit", "nodemailer", + "rimraf", + "sharp", "undici", "vite" ], diff --git a/docs/administration/monitoring.md b/docs/administration/monitoring.md index 5296b795..5d0c81e2 100644 --- a/docs/administration/monitoring.md +++ b/docs/administration/monitoring.md @@ -8,6 +8,8 @@ The minimum production observability stack should include: - `GET /api/health` for liveness/readiness - `GET /api/metrics` for Prometheus scraping + (protected by `METRICS_TOKEN` when set — pass it as a Bearer token in the + scrape config; see `deploy/prometheus.yml`) - Grafana dashboards for request latency, queue health, and storage/DB timing - Alerting for p95 regressions and service degradation diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 00000000..05b937f2 --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,694 @@ +# OpenCodeHub CLI (och) + +The OpenCodeHub CLI (`och`) provides a GitHub CLI-like experience for interacting with OpenCodeHub from the command line. + +## Installation + +```bash +npm install -g opencodehub-cli +``` + +## Quick Start + +```bash +# 1. Login to your server +och auth login --url http://your-server:4321 + +# 2. Create a repository +och repo create my-project --description "My new project" + +# 3. Push your code +och repo push --branch main +``` + +--- + +## Authentication + +### `och auth login` +Login to OpenCodeHub. + +```bash +och auth login --url http://your-server:4321 +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-u, --url ` | OpenCodeHub server URL | +| `-t, --token ` | Personal access token (non-interactive) | +| `--with-token` | Authenticate using interactive token entry | +| `--ca-file ` | Path to custom CA bundle (self-signed TLS) | +| `--insecure` | Disable TLS certificate verification | + +**Examples:** +```bash +# Interactive login +och auth login --url http://localhost:4321 + +# Non-interactive with token +och auth login --url http://localhost:4321 -t och_xxxxx + +# Self-signed TLS +och auth login --url https://git.example.com --ca-file /path/to/ca.pem +``` + +### `och auth logout` +Logout from OpenCodeHub. + +```bash +och auth logout +``` + +### `och auth status` +Show current authentication status. + +```bash +och auth status +``` + +--- + +## Repository Management + +### `och repo create ` +Create a new repository on OpenCodeHub. + +```bash +och repo create my-project --description "A cool project" +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-d, --description ` | Repository description | +| `-p, --private` | Make repository private | +| `--no-init` | Don't initialize with README | + +### `och repo clone ` +Clone a repository from OpenCodeHub. + +```bash +och repo clone owner/repo +och repo clone owner/repo my-local-name +``` + +**Arguments:** +| Argument | Description | +|----------|-------------| +| `` | Repository in `owner/name` format | +| `[destination]` | Local directory name (optional) | + +### `och repo push` +Push local repository to OpenCodeHub. + +```bash +och repo push +och repo push --branch main +och repo push --force +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-b, --branch ` | Branch to push | +| `-f, --force` | Force push (overwrite remote) | + +### `och repo list` +List your repositories. + +```bash +och repo list +``` + +--- + +## Git Operations + +### `och git init` +Initialize a repository for OpenCodeHub. + +```bash +och git init +och git init --url http://your-server:4321 +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-u, --url ` | OpenCodeHub server URL | + +### `och git push` +Push to OpenCodeHub (shorthand for `repo push`). + +```bash +och git push +och git push --branch main --force +``` + +### `och git sync` +Sync with remote (fetch + push). + +```bash +och git sync +och git sync --force +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-f, --force` | Force push after sync | + +--- + +## Pull Requests + +### `och pr create` +Create a new pull request. + +```bash +och pr create +och pr create --title "feat: new feature" --base main +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-t, --title ` | PR title | +| `-b, --base <branch>` | Base branch | +| `-h, --head <branch>` | Head branch | +| `-d, --description <desc>` | PR description | + +### `och pr list` +List pull requests. + +```bash +och pr list +och pr list --state open +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-s, --state <state>` | Filter by state (open, closed, all) | + +### `och pr view <number>` +View a pull request. + +```bash +och pr view 42 +``` + +### `och pr checkout <number>` +Checkout a pull request locally. + +```bash +och pr checkout 42 +``` + +### `och pr merge <number>` +Merge a pull request. + +```bash +och pr merge 42 +``` + +### `och pr close <number>` +Close a pull request. + +```bash +och pr close 42 +``` + +--- + +## Stacked PRs + +### `och stack create <name>` +Create a new branch in the current stack. + +```bash +och stack create feature/child-1 +``` + +### `och stack submit` +Push stack and create/update PRs for all branches. + +```bash +och stack submit +``` + +### `och stack sync` +Rebase stack on updated main. + +```bash +och stack sync +``` + +### `och stack log` +Visualize stack tree. + +```bash +och stack log +``` + +### `och stack status` +Show current stack and branch status. + +```bash +och stack status +``` + +--- + +## Issues + +### `och issue create` +Create a new issue. + +```bash +och issue create --title "Bug: something broken" --body "Description..." +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-t, --title <title>` | Issue title | +| `-b, --body <body>` | Issue body | +| `--labels <labels>` | Comma-separated labels | + +### `och issue list` +List issues. + +```bash +och issue list +och issue list --state open +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-s, --state <state>` | Filter by state (open, closed, all) | + +### `och issue view <number>` +View an issue. + +```bash +och issue view 42 +``` + +### `och issue close <number>` +Close an issue. + +```bash +och issue close 42 +``` + +--- + +## Branches + +### `och branch checkout [branch]` +Checkout a branch (interactive if no branch specified). + +```bash +och branch checkout +och branch checkout main +``` + +### `och branch rename [old-name] <new-name>` +Rename a branch. + +```bash +och branch rename old-name new-name +``` + +--- + +## Merge Queue + +### `och queue add <pr-number>` +Add a pull request to the merge queue. + +```bash +och queue add 42 +``` + +### `och queue list` +List merge queue items. + +```bash +och queue list +``` + +### `och queue remove <pr-number>` +Remove a pull request from the merge queue. + +```bash +och queue remove 42 +``` + +--- + +## CI/CD + +### `och ci list` +List pipeline runs. + +```bash +och ci list +``` + +### `och ci view <run-id>` +View a pipeline run. + +```bash +och ci view run-123 +``` + +### `och ci cancel <run-id>` +Cancel a pipeline run. + +```bash +och ci cancel run-123 +``` + +--- + +## Releases + +### `och release create <tag>` +Create a new release. + +```bash +och release create v1.0.0 --name "Version 1.0.0" --body "Release notes..." +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-n, --name <name>` | Release name | +| `-b, --body <body>` | Release notes | +| `--draft` | Create as draft | +| `--prerelease` | Mark as pre-release | + +### `och release list` +List releases. + +```bash +och release list +``` + +--- + +## Code Review + +### `och review ai <pr-number>` +Trigger AI code review for a pull request. + +```bash +och review ai 42 +``` + +### `och review status <pr-number>` +Check AI review status. + +```bash +och review status 42 +``` + +--- + +## Secrets + +### `och secret set <name>` +Set a secret. + +```bash +och secret set MY_SECRET +``` + +### `och secret list` +List secrets. + +```bash +och secret list +``` + +### `och secret delete <name>` +Delete a secret. + +```bash +och secret delete MY_SECRET +``` + +--- + +## Notifications + +### `och notify list` +List your notifications. + +```bash +och notify list +``` + +### `och notify read [id]` +Mark notification(s) as read. + +```bash +och notify read +och notify read notification-id +``` + +--- + +## SSH Keys + +### `och ssh-key add` +Add an SSH key to your account. + +```bash +och ssh-key add +``` + +### `och ssh-key list` +List your SSH keys. + +```bash +och ssh-key list +``` + +### `och ssh-key delete <id>` +Delete an SSH key. + +```bash +och ssh-key delete key-id +``` + +--- + +## Search + +### `och search repos <query>` +Search repositories. + +```bash +och search repos "machine learning" +``` + +### `och search issues <query>` +Search issues. + +```bash +och search issues "bug fix" +``` + +--- + +## Insights + +### `och insights show` +Show your developer metrics. + +```bash +och insights show +``` + +### `och insights team` +Show team/repository metrics. + +```bash +och insights team +``` + +--- + +## Configuration + +### `och config list` +List all configuration values. + +```bash +och config list +``` + +### `och config get <key>` +Get a configuration value. + +```bash +och config get serverUrl +``` + +### `och config set <key> <value>` +Set a configuration value. + +```bash +och config set serverUrl http://localhost:4321 +``` + +--- + +## Automations + +### `och automate list` +List automation rules for current repository. + +```bash +och automate list +``` + +### `och automate create` +Create a new automation rule. + +```bash +och automate create +``` + +--- + +## Runner + +### `och runner config` +Configure a self-hosted runner interactively. + +```bash +och runner config +``` + +--- + +## API + +### `och api <endpoint>` +Make direct API requests. + +```bash +och api /api/user +och api /api/repos --method POST --data '{"name": "my-repo"}' +``` + +**Options:** +| Option | Description | +|--------|-------------| +| `-m, --method <method>` | HTTP method (GET, POST, PUT, DELETE) | +| `-d, --data <data>` | Request body (JSON) | + +--- + +## Focus Mode + +### `och focus` +Interactive stack and review cockpit. + +```bash +och focus +``` + +A terminal UI for managing PRs, reviews, and merge queue. + +--- + +## Shell Completions + +### `och completion` +Generate shell completions. + +```bash +# Bash +och completion > ~/.bashrc.d/och-completion.bash + +# Zsh +och completion > ~/.zsh/completions/_och + +# Fish +och completion --fish > ~/.config/fish/completions/och.fish +``` + +--- + +## Environment Variables + +| Variable | Description | +|----------|-------------| +| `OCH_TOKEN` | Personal access token (overrides config) | +| `OCH_SERVER_URL` | Server URL (overrides config) | +| `OCH_HTTP_TIMEOUT_MS` | HTTP request timeout (default: 15000) | + +--- + +## Configuration File + +The CLI stores configuration in `~/.config/opencodehub-cli/config.json`: + +```json +{ + "serverUrl": "http://localhost:4321", + "token": "och_xxxxx", + "username": "your-username" +} +``` + +--- + +## Common Workflows + +### New Repository +```bash +och auth login --url http://your-server:4321 +mkdir my-project && cd my-project +git init +och repo create my-project --description "My project" +# Add files and commit +och repo push --branch main +``` + +### Daily Development +```bash +och git sync # Pull latest changes +och branch checkout feature-x # Switch branch +# Make changes and commit +och git push # Push changes +och pr create --title "feat: X" # Create PR +``` + +### Stacked PRs +```bash +och stack create feature/part-1 # Create first branch +# Work on part 1 +och stack submit # Push & create PR + +och stack create feature/part-2 # Create child branch +# Work on part 2 +och stack submit # Push & create PR + +och stack log # View stack hierarchy +``` + +### Code Review +```bash +och pr list # See open PRs +och pr checkout 42 # Checkout PR branch +# Review code +och pr merge 42 # Merge when ready +``` diff --git a/docs/development/contributing.md b/docs/development/contributing.md index b2c5b653..6d8bcfc1 100644 --- a/docs/development/contributing.md +++ b/docs/development/contributing.md @@ -49,7 +49,7 @@ Start the development server: bun dev ``` -Visit `http://localhost:3000`. +Visit `http://localhost:4321`. --- diff --git a/docs/development/release-gates.md b/docs/development/release-gates.md new file mode 100644 index 00000000..b454efd2 --- /dev/null +++ b/docs/development/release-gates.md @@ -0,0 +1,21 @@ +# Release Gates + +This document outlines the required gates that must be passed before any code can be merged into the default branch. + +## 1. Continuous Integration (CI) +All pull requests must pass the following CI checks: +- **Linting**: `npm run lint` must exit with 0. +- **Typechecking**: `npm run typecheck` must exit with 0. +- **Tests**: Unit, integration, and E2E tests (`npm run test`) must all pass. + +## 2. Security Scans +- **Secret Scanning**: Commits must be scanned for leaked secrets. +- **Dependency Audit**: `npm audit` must not report vulnerabilities above the defined threshold. +- **Authorization Regression**: Core RBAC and permissions tests must pass. + +## 3. Documentation Parity +Any feature addition must include updates to both documentation stacks: +- The Markdown docs in `docs/` +- The Starlight site in `docs-site/src/content/docs/` + +Features will not be merged if their documentation is missing or incomplete. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 4f861242..f5b91ae7 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -47,7 +47,7 @@ INTERNAL_HOOK_SECRET=<openssl rand -hex 32> # Domain Configuration SITE_URL=https://git.yourcompany.com -PORT=3000 +PORT=4321 # Database (Using the Postgres container defined in compose) DATABASE_URL=postgresql://opencodehub:securepassword@postgres:5432/opencodehub @@ -98,7 +98,7 @@ server { client_max_body_size 500M; location / { - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4321; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index a7a9cd6d..9e86abff 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -3,7 +3,7 @@ Welcome to OpenCodeHub! This guide will help you navigate the platform and start collaborating in minutes. ## 1. Login -Navigate to your instance URL (e.g., `http://localhost:3000`) and log in with the admin credentials you created during installation. +Navigate to your instance URL (e.g., `http://localhost:4321`) and log in with the admin credentials you created during installation. ![Login Screen](../../assets/login-screen.png) diff --git a/docs/getting-started/ssh-keys.md b/docs/getting-started/ssh-keys.md new file mode 100644 index 00000000..3f4d231b --- /dev/null +++ b/docs/getting-started/ssh-keys.md @@ -0,0 +1,120 @@ +--- +title: "SSH Keys & Deploy Keys" +description: "How to set up SSH keys for secure Git operations and deploy keys for CI/CD" +--- + +# SSH Keys & Deploy Keys + +OpenCodeHub supports SSH key authentication for secure Git operations without passwords. + +## SSH Keys (Account-Level) + +SSH keys give you access to **all your repositories**. Use them for daily development. + +### Generating an SSH Key + +**Ed25519 (recommended):** +```bash +ssh-keygen -t ed25519 -C "your_email@example.com" +``` + +**RSA (legacy):** +```bash +ssh-keygen -t rsa -b 4096 -C "your_email@example.com" +``` + +### Adding Your Key + +1. Copy your public key: + ```bash + cat ~/.ssh/id_ed25519.pub + ``` + +2. Go to **Settings → SSH Keys** in OpenCodeHub + +3. Click **New SSH Key** + +4. Paste your public key and give it a title (e.g., "Work Laptop") + +### Testing Your Connection + +```bash +ssh -T git@your-server.com +``` + +You should see: `Hi username! You've successfully authenticated` + +> **Note:** Replace `your-server.com` with your actual OpenCodeHub server hostname or IP address. + +### Using SSH with Git + +```bash +# Clone via SSH +git clone git@your-server.com:owner/repo.git + +# Add SSH remote to existing repo +git remote add origin git@your-server.com:owner/repo.git +``` + +## Deploy Keys (Repository-Level) + +Deploy keys grant access to a **single repository**. Use them for CI/CD and deployments. + +### When to Use Deploy Keys + +- CI/CD pipelines that need to pull code +- Deployment servers +- Automation scripts with limited access + +### Generating a Deploy Key + +```bash +ssh-keygen -t ed25519 -C "deploy@production-server" +``` + +### Adding a Deploy Key + +1. Go to **Repository → Settings → Deploy Keys** + +2. Click **Add deploy key** + +3. Paste the public key and give it a title + +4. Choose **Read-only** or **Read/write** access + +### Key Differences + +| Feature | SSH Keys | Deploy Keys | +|---------|----------|-------------| +| Scope | All repositories | Single repository | +| Access | Full account access | Read-only or read/write | +| Use case | Daily development | CI/CD, deployment | + +## Troubleshooting + +### Permission Denied + +```bash +# Check SSH agent +ssh-add -l + +# Add key to agent +ssh-add ~/.ssh/id_ed25519 + +# Debug SSH connection +ssh -vvv git@your-server.com +``` + +### Wrong Key Being Used + +```bash +# Specify key explicitly +ssh -i ~/.ssh/id_ed25519 git@your-server.com +``` + +### Host Key Verification Failed + +```bash +# Add host to known_hosts +ssh-keyscan your-server.com >> ~/.ssh/known_hosts +``` diff --git a/docs/reference/api-versioning.md b/docs/reference/api-versioning.md new file mode 100644 index 00000000..13ba57e5 --- /dev/null +++ b/docs/reference/api-versioning.md @@ -0,0 +1,14 @@ +# API Versioning + +OpenCodeHub provides a stable REST API and GraphQL endpoint. + +## Versioning Strategy +Currently, the REST API does not have explicit `/v1/` prefixes in the route paths. Instead, the API follows a continuous evolution model where breaking changes are heavily minimized. + +When breaking changes are unavoidable: +- They will be announced in the release notes. +- A deprecation header (`Deprecation: <date>`) will be added to the response of the old endpoint. +- Clients will have at least one minor release cycle to migrate. + +## GraphQL +The GraphQL API inherently supports seamless evolution by deprecating specific fields using the `@deprecated` directive, allowing clients to transition without breaking version changes. diff --git a/drizzle/0000_abandoned_king_bedlam.sql b/drizzle/0000_abandoned_king_bedlam.sql deleted file mode 100644 index 6bfbc05e..00000000 --- a/drizzle/0000_abandoned_king_bedlam.sql +++ /dev/null @@ -1,36 +0,0 @@ -ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "ai_config" text; - -CREATE TABLE IF NOT EXISTS "organizations" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "display_name" text, - "description" text, - "email" text, - "location" text, - "website" text, - "avatar_url" text, - "is_verified" boolean DEFAULT false, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - CONSTRAINT "organizations_name_unique" UNIQUE("name") -); - -CREATE TABLE IF NOT EXISTS "organization_members" ( - "organization_id" text NOT NULL, - "user_id" text NOT NULL, - "role" text DEFAULT 'member' NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - CONSTRAINT "organization_members_organization_id_user_id_pk" PRIMARY KEY("organization_id","user_id") -); - -DO $$ BEGIN - ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; - -DO $$ BEGIN - ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; \ No newline at end of file diff --git a/drizzle/0000_black_monster_badoon.sql b/drizzle/0000_black_monster_badoon.sql deleted file mode 100644 index 4f2a4431..00000000 --- a/drizzle/0000_black_monster_badoon.sql +++ /dev/null @@ -1,1055 +0,0 @@ -CREATE TABLE `email_verification_tokens` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `token` text NOT NULL, - `expires_at` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE UNIQUE INDEX `email_verification_tokens_token_unique` ON `email_verification_tokens` (`token`);--> statement-breakpoint -CREATE TABLE `gpg_keys` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `name` text NOT NULL, - `key_id` text NOT NULL, - `public_key` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `oauth_accounts` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `provider` text NOT NULL, - `provider_account_id` text NOT NULL, - `access_token` text, - `refresh_token` text, - `expires_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `password_reset_tokens` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `token` text NOT NULL, - `expires_at` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE UNIQUE INDEX `password_reset_tokens_token_unique` ON `password_reset_tokens` (`token`);--> statement-breakpoint -CREATE TABLE `personal_access_tokens` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `name` text NOT NULL, - `token` text NOT NULL, - `expires_at` text, - `last_used_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE UNIQUE INDEX `personal_access_tokens_token_unique` ON `personal_access_tokens` (`token`);--> statement-breakpoint -CREATE TABLE `sessions` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `token` text NOT NULL, - `user_agent` text, - `ip_address` text, - `expires_at` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint -CREATE TABLE `ssh_keys` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `title` text NOT NULL, - `fingerprint` text NOT NULL, - `public_key` text NOT NULL, - `key_type` text NOT NULL, - `last_used_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE UNIQUE INDEX `ssh_keys_fingerprint_unique` ON `ssh_keys` (`fingerprint`);--> statement-breakpoint -CREATE TABLE `users` ( - `id` text PRIMARY KEY NOT NULL, - `username` text NOT NULL, - `email` text NOT NULL, - `password_hash` text, - `display_name` text, - `bio` text, - `avatar_url` text, - `location` text, - `website` text, - `company` text, - `is_admin` integer DEFAULT false, - `is_active` integer DEFAULT true, - `email_verified` integer DEFAULT false, - `two_factor_enabled` integer DEFAULT false, - `two_factor_secret` text, - `last_login_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL -); ---> statement-breakpoint -CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint -CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint -CREATE TABLE `branches` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text NOT NULL, - `commit_sha` text NOT NULL, - `is_protected` integer DEFAULT false, - `is_default` integer DEFAULT false, - `protection_rules` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `commits` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `sha` text NOT NULL, - `message` text NOT NULL, - `author_name` text NOT NULL, - `author_email` text NOT NULL, - `author_date` text NOT NULL, - `committer_name` text NOT NULL, - `committer_email` text NOT NULL, - `committer_date` text NOT NULL, - `parent_shas` text, - `tree_sha` text, - `user_id` text, - `stats` text, - `signature` text, - `is_verified` integer DEFAULT false, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `releases` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `tag_id` text, - `name` text NOT NULL, - `body` text, - `is_draft` integer DEFAULT false, - `is_prerelease` integer DEFAULT false, - `author_id` text, - `assets` text, - `download_count` integer DEFAULT 0, - `published_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `repositories` ( - `id` text PRIMARY KEY NOT NULL, - `name` text NOT NULL, - `slug` text NOT NULL, - `description` text, - `owner_id` text NOT NULL, - `owner_type` text DEFAULT 'user' NOT NULL, - `visibility` text DEFAULT 'public' NOT NULL, - `default_branch` text DEFAULT 'main' NOT NULL, - `disk_path` text NOT NULL, - `ssh_clone_url` text, - `http_clone_url` text, - `star_count` integer DEFAULT 0, - `fork_count` integer DEFAULT 0, - `watch_count` integer DEFAULT 0, - `open_issue_count` integer DEFAULT 0, - `open_pr_count` integer DEFAULT 0, - `size` integer DEFAULT 0, - `is_fork` integer DEFAULT false, - `forked_from_id` text, - `is_archived` integer DEFAULT false, - `is_mirror` integer DEFAULT false, - `mirror_url` text, - `has_issues` integer DEFAULT true, - `has_wiki` integer DEFAULT true, - `has_actions` integer DEFAULT true, - `allow_forking` integer DEFAULT true, - `license_type` text, - `topics` text, - `language` text, - `languages` text, - `last_activity_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`forked_from_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `repository_collaborators` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `user_id` text NOT NULL, - `role` text DEFAULT 'developer' NOT NULL, - `added_by_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `repository_stars` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `user_id` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `repository_watchers` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `user_id` text NOT NULL, - `watch_level` text DEFAULT 'watching' NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `tags` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text NOT NULL, - `commit_sha` text NOT NULL, - `message` text, - `tagger_name` text, - `tagger_email` text, - `tagged_at` text, - `is_release` integer DEFAULT false, - `release_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `webhooks` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `url` text NOT NULL, - `secret` text, - `events` text NOT NULL, - `is_active` integer DEFAULT true, - `last_delivery_at` text, - `last_delivery_status` integer, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `issue_assignees` ( - `id` text PRIMARY KEY NOT NULL, - `issue_id` text NOT NULL, - `user_id` text NOT NULL, - `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `issue_comments` ( - `id` text PRIMARY KEY NOT NULL, - `issue_id` text NOT NULL, - `author_id` text NOT NULL, - `body` text NOT NULL, - `reactions` text, - `is_edited` integer DEFAULT false, - `edited_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `issue_labels` ( - `id` text PRIMARY KEY NOT NULL, - `issue_id` text NOT NULL, - `label_id` text NOT NULL, - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `issue_subscribers` ( - `id` text PRIMARY KEY NOT NULL, - `issue_id` text NOT NULL, - `user_id` text NOT NULL, - `subscribed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `issues` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `number` integer NOT NULL, - `title` text NOT NULL, - `body` text, - `state` text DEFAULT 'open' NOT NULL, - `author_id` text NOT NULL, - `assignee_id` text, - `milestone_id` text, - `is_pinned` integer DEFAULT false, - `is_locked` integer DEFAULT false, - `lock_reason` text, - `comment_count` integer DEFAULT 0, - `reactions` text, - `closed_at` text, - `closed_by_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `labels` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text NOT NULL, - `color` text DEFAULT '#6b7280' NOT NULL, - `description` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `milestones` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `title` text NOT NULL, - `description` text, - `state` text DEFAULT 'open' NOT NULL, - `due_date` text, - `open_issue_count` integer DEFAULT 0, - `closed_issue_count` integer DEFAULT 0, - `closed_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pull_request_assignees` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `user_id` text NOT NULL, - `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pull_request_checks` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `name` text NOT NULL, - `status` text NOT NULL, - `conclusion` text, - `head_sha` text NOT NULL, - `external_id` text, - `details_url` text, - `output` text, - `started_at` text, - `completed_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pull_request_comments` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `review_id` text, - `author_id` text NOT NULL, - `body` text NOT NULL, - `path` text, - `line` integer, - `side` text, - `start_line` integer, - `start_side` text, - `commit_sha` text, - `original_commit_sha` text, - `original_line` integer, - `in_reply_to_id` text, - `reactions` text, - `is_resolved` integer DEFAULT false, - `resolved_by_id` text, - `resolved_at` text, - `is_edited` integer DEFAULT false, - `edited_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`review_id`) REFERENCES `pull_request_reviews`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`in_reply_to_id`) REFERENCES `pull_request_comments`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`resolved_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `pull_request_labels` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `label_id` text NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pull_request_reviewers` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `user_id` text NOT NULL, - `is_required` integer DEFAULT false, - `requested_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pull_request_reviews` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `reviewer_id` text NOT NULL, - `state` text NOT NULL, - `body` text, - `commit_sha` text, - `submitted_at` text, - `dismissed_at` text, - `dismissed_by_id` text, - `dismissal_reason` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`reviewer_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `pull_requests` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `number` integer NOT NULL, - `title` text NOT NULL, - `body` text, - `state` text DEFAULT 'open' NOT NULL, - `author_id` text NOT NULL, - `assignee_id` text, - `milestone_id` text, - `head_branch` text NOT NULL, - `head_sha` text NOT NULL, - `head_repository_id` text, - `base_branch` text NOT NULL, - `base_sha` text NOT NULL, - `is_draft` integer DEFAULT false, - `is_merged` integer DEFAULT false, - `merged_at` text, - `merged_by_id` text, - `merge_sha` text, - `merge_method` text, - `additions` integer DEFAULT 0, - `deletions` integer DEFAULT 0, - `changed_files` integer DEFAULT 0, - `comment_count` integer DEFAULT 0, - `review_count` integer DEFAULT 0, - `mergeable` integer, - `mergeable_state` text, - `rebaseable` integer, - `maintainer_can_modify` integer DEFAULT true, - `allow_auto_merge` integer DEFAULT false, - `auto_merge_method` text, - `closed_at` text, - `closed_by_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`head_repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`merged_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `scheduled_workflows` ( - `id` text PRIMARY KEY NOT NULL, - `workflow_id` text NOT NULL, - `cron_expression` text NOT NULL, - `timezone` text DEFAULT 'UTC', - `is_enabled` integer DEFAULT true, - `last_run_at` text, - `next_run_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `workflow_artifacts` ( - `id` text PRIMARY KEY NOT NULL, - `run_id` text NOT NULL, - `job_id` text, - `name` text NOT NULL, - `size_bytes` integer NOT NULL, - `storage_path` text NOT NULL, - `mime_type` text, - `download_count` integer DEFAULT 0, - `expires_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `workflow_jobs` ( - `id` text PRIMARY KEY NOT NULL, - `run_id` text NOT NULL, - `name` text NOT NULL, - `status` text DEFAULT 'queued' NOT NULL, - `conclusion` text, - `runner_id` text, - `runner_name` text, - `runner_group_id` text, - `runner_group_name` text, - `container_id` text, - `container_image` text, - `needs` text, - `environment` text, - `environment_url` text, - `matrix` text, - `started_at` text, - `completed_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `workflow_logs` ( - `id` text PRIMARY KEY NOT NULL, - `job_id` text NOT NULL, - `step_id` text, - `log_level` text DEFAULT 'info' NOT NULL, - `message` text NOT NULL, - `timestamp` text NOT NULL, - `line_number` integer, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`step_id`) REFERENCES `workflow_steps`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `workflow_runs` ( - `id` text PRIMARY KEY NOT NULL, - `workflow_id` text NOT NULL, - `repository_id` text NOT NULL, - `run_number` integer NOT NULL, - `run_attempt` integer DEFAULT 1 NOT NULL, - `name` text NOT NULL, - `display_title` text, - `status` text DEFAULT 'queued' NOT NULL, - `conclusion` text, - `event` text NOT NULL, - `head_branch` text, - `head_sha` text NOT NULL, - `base_branch` text, - `base_sha` text, - `pull_request_id` text, - `triggered_by_id` text, - `started_at` text, - `completed_at` text, - `workflow_config` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `workflow_secrets` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text NOT NULL, - `encrypted_value` text NOT NULL, - `environment` text, - `created_by_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `workflow_steps` ( - `id` text PRIMARY KEY NOT NULL, - `job_id` text NOT NULL, - `number` integer NOT NULL, - `name` text NOT NULL, - `status` text DEFAULT 'queued' NOT NULL, - `conclusion` text, - `uses` text, - `run` text, - `shell` text, - `working_directory` text, - `env` text, - `with` text, - `if` text, - `continue_on_error` integer DEFAULT false, - `timeout_minutes` integer, - `started_at` text, - `completed_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `workflow_variables` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text NOT NULL, - `value` text NOT NULL, - `environment` text, - `created_by_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `workflows` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text NOT NULL, - `path` text NOT NULL, - `state` text DEFAULT 'active' NOT NULL, - `badge_url` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `wiki_pages` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `slug` text NOT NULL, - `title` text NOT NULL, - `content` text NOT NULL, - `format` text DEFAULT 'markdown' NOT NULL, - `parent_id` text, - `order` integer DEFAULT 0, - `last_editor_id` text, - `view_count` integer DEFAULT 0, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`parent_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`last_editor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `wiki_revisions` ( - `id` text PRIMARY KEY NOT NULL, - `page_id` text NOT NULL, - `content` text NOT NULL, - `message` text, - `author_id` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`page_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `organization_members` ( - `id` text PRIMARY KEY NOT NULL, - `organization_id` text NOT NULL, - `user_id` text NOT NULL, - `role` text DEFAULT 'member' NOT NULL, - `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `organizations` ( - `id` text PRIMARY KEY NOT NULL, - `name` text NOT NULL, - `display_name` text, - `description` text, - `avatar_url` text, - `website` text, - `location` text, - `email` text, - `is_verified` integer DEFAULT false, - `visibility` text DEFAULT 'public' NOT NULL, - `default_repo_permission` text DEFAULT 'read', - `members_can_create_repos` integer DEFAULT true, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL -); ---> statement-breakpoint -CREATE UNIQUE INDEX `organizations_name_unique` ON `organizations` (`name`);--> statement-breakpoint -CREATE TABLE `team_members` ( - `id` text PRIMARY KEY NOT NULL, - `team_id` text NOT NULL, - `user_id` text NOT NULL, - `role` text DEFAULT 'member' NOT NULL, - `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `team_repositories` ( - `id` text PRIMARY KEY NOT NULL, - `team_id` text NOT NULL, - `repository_id` text NOT NULL, - `permission` text DEFAULT 'read' NOT NULL, - `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `teams` ( - `id` text PRIMARY KEY NOT NULL, - `organization_id` text NOT NULL, - `name` text NOT NULL, - `slug` text NOT NULL, - `description` text, - `privacy` text DEFAULT 'visible' NOT NULL, - `parent_id` text, - `permission` text DEFAULT 'read' NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`parent_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `activities` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `repository_id` text, - `organization_id` text, - `type` text NOT NULL, - `action` text NOT NULL, - `ref_type` text, - `ref_name` text, - `target_type` text, - `target_id` text, - `payload` text, - `is_public` integer DEFAULT true, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `audit_logs` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text, - `organization_id` text, - `repository_id` text, - `action` text NOT NULL, - `actor_type` text DEFAULT 'user' NOT NULL, - `actor_id` text, - `actor_ip` text, - `actor_user_agent` text, - `target_type` text, - `target_id` text, - `target_name` text, - `data` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `notifications` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `repository_id` text, - `type` text NOT NULL, - `title` text NOT NULL, - `body` text, - `url` text, - `actor_id` text, - `subject_type` text, - `subject_id` text, - `reason` text NOT NULL, - `is_read` integer DEFAULT false, - `read_at` text, - `is_archived` integer DEFAULT false, - `archived_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`actor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `security_scans` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `status` text DEFAULT 'queued' NOT NULL, - `started_at` integer, - `completed_at` integer, - `critical_count` integer DEFAULT 0, - `high_count` integer DEFAULT 0, - `medium_count` integer DEFAULT 0, - `low_count` integer DEFAULT 0, - `unknown_count` integer DEFAULT 0, - `logs` text, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `security_vulnerabilities` ( - `id` text PRIMARY KEY NOT NULL, - `scan_id` text NOT NULL, - `vulnerability_id` text NOT NULL, - `pkg_name` text NOT NULL, - `installed_version` text, - `fixed_version` text, - `severity` text NOT NULL, - `title` text, - `description` text, - `target` text, - `class` text, - FOREIGN KEY (`scan_id`) REFERENCES `security_scans`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pipeline_runners` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text, - `token` text NOT NULL, - `name` text NOT NULL, - `os` text, - `arch` text, - `version` text, - `status` text DEFAULT 'offline' NOT NULL, - `last_seen_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `pr_stack_entries` ( - `id` text PRIMARY KEY NOT NULL, - `stack_id` text NOT NULL, - `pull_request_id` text NOT NULL, - `stack_order` integer NOT NULL, - `parent_pr_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`parent_pr_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `pr_stacks` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `name` text, - `base_branch` text NOT NULL, - `status` text DEFAULT 'active' NOT NULL, - `created_by_id` text NOT NULL, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `merge_queue` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `pull_request_id` text NOT NULL, - `stack_id` text, - `status` text DEFAULT 'pending' NOT NULL, - `priority` integer DEFAULT 0 NOT NULL, - `position` integer, - `ci_status` text DEFAULT 'pending', - `ci_run_id` text, - `added_by_id` text NOT NULL, - `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `estimated_merge_at` text, - `started_at` text, - `completed_at` text, - `failure_reason` text, - `retry_count` integer DEFAULT 0, - `merge_method` text DEFAULT 'merge', - `delete_on_merge` integer DEFAULT true, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `ai_review_suggestions` ( - `id` text PRIMARY KEY NOT NULL, - `ai_review_id` text NOT NULL, - `path` text NOT NULL, - `line` integer, - `end_line` integer, - `severity` text NOT NULL, - `type` text NOT NULL, - `category` text, - `title` text NOT NULL, - `message` text NOT NULL, - `suggested_fix` text, - `explanation` text, - `is_applied` integer DEFAULT false, - `is_dismissed` integer DEFAULT false, - `applied_at` text, - `applied_by_id` text, - `dismissed_at` text, - `dismissed_by_id` text, - `dismiss_reason` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`ai_review_id`) REFERENCES `ai_reviews`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`applied_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, - FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `ai_reviews` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `status` text DEFAULT 'pending' NOT NULL, - `model` text NOT NULL, - `provider` text NOT NULL, - `stack_context` text, - `includes_stack_context` integer DEFAULT false, - `summary` text, - `overall_severity` text, - `suggestions_count` integer DEFAULT 0, - `tokens_used` integer, - `prompt_tokens` integer, - `completion_tokens` integer, - `cost_cents` integer, - `raw_response` text, - `triggered_by_id` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `started_at` text, - `completed_at` text, - `error_message` text, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `pr_metrics` ( - `id` text PRIMARY KEY NOT NULL, - `pull_request_id` text NOT NULL, - `repository_id` text NOT NULL, - `author_id` text NOT NULL, - `time_to_first_review` integer, - `time_to_approval` integer, - `time_to_merge` integer, - `total_cycle_time` integer, - `review_rounds` integer DEFAULT 1, - `reviewers_count` integer DEFAULT 0, - `comments_count` integer DEFAULT 0, - `changes_requested_count` integer DEFAULT 0, - `lines_added` integer DEFAULT 0, - `lines_removed` integer DEFAULT 0, - `files_changed` integer DEFAULT 0, - `commits` integer DEFAULT 1, - `is_stacked` integer DEFAULT false, - `stack_position` integer, - `pr_created_at` text, - `first_review_at` text, - `approved_at` text, - `merged_at` text, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `repo_metrics` ( - `id` text PRIMARY KEY NOT NULL, - `repository_id` text NOT NULL, - `week_of` text NOT NULL, - `prs_opened` integer DEFAULT 0, - `prs_merged` integer DEFAULT 0, - `prs_closed` integer DEFAULT 0, - `avg_time_to_first_review` integer, - `avg_time_to_merge` integer, - `avg_review_rounds` integer, - `avg_lines_changed` integer, - `avg_files_changed` integer, - `stacked_prs` integer DEFAULT 0, - `stacked_prs_percentage` integer DEFAULT 0, - `active_authors` integer DEFAULT 0, - `active_reviewers` integer DEFAULT 0, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `review_metrics` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `repository_id` text, - `organization_id` text, - `week_of` text NOT NULL, - `prs_authored` integer DEFAULT 0, - `prs_authored_merged` integer DEFAULT 0, - `avg_time_to_merge_authored` integer, - `lines_authored_added` integer DEFAULT 0, - `lines_authored_removed` integer DEFAULT 0, - `prs_reviewed` integer DEFAULT 0, - `avg_time_to_review` integer, - `comments_given` integer DEFAULT 0, - `approvals_given` integer DEFAULT 0, - `changes_requested_given` integer DEFAULT 0, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE TABLE `slack_channel_mappings` ( - `id` text PRIMARY KEY NOT NULL, - `workspace_id` text NOT NULL, - `repository_id` text NOT NULL, - `channel_id` text NOT NULL, - `channel_name` text, - `notify_on` text DEFAULT '["pr_created","pr_merged","review_requested","ci_failed"]', - `notify_branches` text, - `notify_authors` text, - `is_active` integer DEFAULT true, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `slack_user_mappings` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `workspace_id` text NOT NULL, - `slack_user_id` text NOT NULL, - `slack_username` text, - `dm_preferences` text DEFAULT '{"review_requested":true,"pr_approved":true,"ci_failed":true}', - `dnd_enabled` integer DEFAULT false, - `dnd_start` text, - `dnd_end` text, - `dnd_timezone` text, - `is_active` integer DEFAULT true, - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `slack_workspaces` ( - `id` text PRIMARY KEY NOT NULL, - `organization_id` text, - `team_id` text NOT NULL, - `team_name` text, - `team_domain` text, - `access_token` text NOT NULL, - `bot_user_id` text, - `bot_access_token` text, - `scopes` text, - `installed_by_id` text NOT NULL, - `installed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, - `is_active` integer DEFAULT true, - `last_used_at` text, - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`installed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); diff --git a/drizzle/0000_jittery_bedlam.sql b/drizzle/0000_jittery_bedlam.sql new file mode 100644 index 00000000..54527cb3 --- /dev/null +++ b/drizzle/0000_jittery_bedlam.sql @@ -0,0 +1,2239 @@ +CREATE TABLE "email_verification_tokens" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "token" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "email_verification_tokens_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "gpg_keys" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "name" text NOT NULL, + "key_id" text NOT NULL, + "public_key" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "oauth_accounts" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "provider" text NOT NULL, + "provider_account_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "expires_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "password_reset_tokens" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "token" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "password_reset_tokens_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "personal_access_tokens" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "name" text NOT NULL, + "token" text NOT NULL, + "expires_at" timestamp, + "last_used_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "personal_access_tokens_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "sessions" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "token" text NOT NULL, + "user_agent" text, + "ip_address" text, + "expires_at" timestamp NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "sessions_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "ssh_keys" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "title" text NOT NULL, + "fingerprint" text NOT NULL, + "public_key" text NOT NULL, + "key_type" text NOT NULL, + "last_used_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "ssh_keys_fingerprint_unique" UNIQUE("fingerprint") +); +--> statement-breakpoint +CREATE TABLE "user_followers" ( + "follower_id" text NOT NULL, + "followee_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "user_followers_follower_id_followee_id_pk" PRIMARY KEY("follower_id","followee_id") +); +--> statement-breakpoint +CREATE TABLE "users" ( + "id" text PRIMARY KEY NOT NULL, + "username" text NOT NULL, + "email" text NOT NULL, + "password_hash" text, + "display_name" text, + "bio" text, + "avatar_url" text, + "location" text, + "website" text, + "company" text, + "is_admin" boolean DEFAULT false, + "is_active" boolean DEFAULT true, + "email_verified" boolean DEFAULT false, + "two_factor_enabled" boolean DEFAULT false, + "two_factor_secret" text, + "last_login_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "ai_config" text, + CONSTRAINT "users_username_unique" UNIQUE("username"), + CONSTRAINT "users_email_unique" UNIQUE("email") +); +--> statement-breakpoint +CREATE TABLE "deploy_keys" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "title" text NOT NULL, + "key" text NOT NULL, + "fingerprint" text NOT NULL, + "read_only" boolean DEFAULT true NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "last_used_at" timestamp +); +--> statement-breakpoint +CREATE TABLE "branches" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "commit_sha" text NOT NULL, + "is_protected" boolean DEFAULT false, + "is_default" boolean DEFAULT false, + "protection_rules" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "commits" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "sha" text NOT NULL, + "message" text NOT NULL, + "author_name" text NOT NULL, + "author_email" text NOT NULL, + "author_date" timestamp NOT NULL, + "committer_name" text NOT NULL, + "committer_email" text NOT NULL, + "committer_date" timestamp NOT NULL, + "parent_shas" text, + "tree_sha" text, + "user_id" text, + "stats" text, + "signature" text, + "is_verified" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "releases" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "tag_id" text, + "name" text NOT NULL, + "body" text, + "is_draft" boolean DEFAULT false, + "is_prerelease" boolean DEFAULT false, + "author_id" text, + "assets" text, + "download_count" integer DEFAULT 0, + "published_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "repositories" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "slug" text NOT NULL, + "description" text, + "owner_id" text NOT NULL, + "owner_type" text DEFAULT 'user' NOT NULL, + "visibility" text DEFAULT 'public' NOT NULL, + "default_branch" text DEFAULT 'main' NOT NULL, + "disk_path" text NOT NULL, + "ssh_clone_url" text, + "http_clone_url" text, + "website" text, + "star_count" integer DEFAULT 0 NOT NULL, + "fork_count" integer DEFAULT 0 NOT NULL, + "watch_count" integer DEFAULT 0 NOT NULL, + "open_issue_count" integer DEFAULT 0 NOT NULL, + "open_pr_count" integer DEFAULT 0 NOT NULL, + "size" integer DEFAULT 0 NOT NULL, + "is_fork" boolean DEFAULT false, + "forked_from_id" text, + "is_archived" boolean DEFAULT false, + "is_template" boolean DEFAULT false, + "is_mirror" boolean DEFAULT false, + "mirror_url" text, + "last_mirror_sync_at" timestamp, + "mirror_sync_status" text, + "has_issues" boolean DEFAULT true, + "has_wiki" boolean DEFAULT true, + "has_actions" boolean DEFAULT true, + "allow_forking" boolean DEFAULT true, + "license_type" text, + "topics" text, + "language" text, + "languages" text, + "last_activity_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "repository_collaborators" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "user_id" text NOT NULL, + "role" text DEFAULT 'developer' NOT NULL, + "added_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "repository_stars" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "user_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "repository_watchers" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "user_id" text NOT NULL, + "watch_level" text DEFAULT 'watching' NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "tags" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "commit_sha" text NOT NULL, + "message" text, + "tagger_name" text, + "tagger_email" text, + "tagged_at" timestamp, + "is_release" boolean DEFAULT false, + "release_id" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_assignees" ( + "id" text PRIMARY KEY NOT NULL, + "issue_id" text NOT NULL, + "user_id" text NOT NULL, + "assigned_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_comments" ( + "id" text PRIMARY KEY NOT NULL, + "issue_id" text NOT NULL, + "author_id" text NOT NULL, + "body" text NOT NULL, + "reactions" text, + "is_edited" boolean DEFAULT false, + "edited_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_labels" ( + "id" text PRIMARY KEY NOT NULL, + "issue_id" text NOT NULL, + "label_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_subscribers" ( + "id" text PRIMARY KEY NOT NULL, + "issue_id" text NOT NULL, + "user_id" text NOT NULL, + "subscribed_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issues" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "number" integer NOT NULL, + "title" text NOT NULL, + "body" text, + "state" text DEFAULT 'open' NOT NULL, + "status_id" text, + "type" text DEFAULT 'issue' NOT NULL, + "parent_id" text, + "author_id" text NOT NULL, + "assignee_id" text, + "milestone_id" text, + "is_pinned" boolean DEFAULT false, + "is_locked" boolean DEFAULT false, + "lock_reason" text, + "comment_count" integer DEFAULT 0, + "reactions" text, + "closed_at" timestamp, + "closed_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "labels" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "color" text DEFAULT '#6b7280' NOT NULL, + "description" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "milestones" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "title" text NOT NULL, + "description" text, + "state" text DEFAULT 'open' NOT NULL, + "due_date" timestamp, + "open_issue_count" integer DEFAULT 0, + "closed_issue_count" integer DEFAULT 0, + "closed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_request_assignees" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "user_id" text NOT NULL, + "assigned_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_request_checks" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "name" text NOT NULL, + "status" text NOT NULL, + "conclusion" text, + "head_sha" text NOT NULL, + "external_id" text, + "details_url" text, + "output" text, + "started_at" timestamp, + "completed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_request_comments" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "review_id" text, + "author_id" text NOT NULL, + "body" text NOT NULL, + "path" text, + "line" integer, + "side" text, + "start_line" integer, + "start_side" text, + "commit_sha" text, + "original_commit_sha" text, + "original_line" integer, + "in_reply_to_id" text, + "reactions" text, + "is_resolved" boolean DEFAULT false, + "resolved_by_id" text, + "resolved_at" timestamp, + "is_edited" boolean DEFAULT false, + "edited_at" timestamp, + "suggestion_content" text, + "suggestion_applied" boolean DEFAULT false, + "suggestion_applied_by_id" text, + "suggestion_applied_at" timestamp, + "suggestion_commit_sha" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_request_labels" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "label_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_request_reviewers" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "user_id" text NOT NULL, + "is_required" boolean DEFAULT false, + "requested_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_request_reviews" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "reviewer_id" text NOT NULL, + "state" text NOT NULL, + "body" text, + "commit_sha" text, + "submitted_at" timestamp, + "dismissed_at" timestamp, + "dismissed_by_id" text, + "dismissal_reason" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pull_requests" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "number" integer NOT NULL, + "title" text NOT NULL, + "body" text, + "state" text DEFAULT 'open' NOT NULL, + "author_id" text NOT NULL, + "assignee_id" text, + "milestone_id" text, + "head_branch" text NOT NULL, + "head_sha" text NOT NULL, + "head_repository_id" text, + "base_branch" text NOT NULL, + "base_sha" text NOT NULL, + "is_draft" boolean DEFAULT false, + "is_merged" boolean DEFAULT false, + "merged_at" timestamp, + "merged_by_id" text, + "merge_commit_sha" text, + "merge_sha" text, + "merge_method" text, + "additions" integer DEFAULT 0, + "deletions" integer DEFAULT 0, + "changed_files" integer DEFAULT 0, + "comment_count" integer DEFAULT 0, + "review_count" integer DEFAULT 0, + "mergeable" boolean, + "mergeable_state" text, + "rebaseable" boolean, + "maintainer_can_modify" boolean DEFAULT true, + "allow_auto_merge" boolean DEFAULT false, + "auto_merge_method" text, + "auto_merge_enabled_by_id" text, + "auto_merge_enabled_at" timestamp, + "state_id" text, + "custom_state_changed_at" timestamp, + "closed_at" timestamp, + "closed_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "project_cards" ( + "id" text PRIMARY KEY NOT NULL, + "column_id" text NOT NULL, + "content_id" text, + "content_type" text, + "note" text, + "order" integer NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "creator_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "project_columns" ( + "id" text PRIMARY KEY NOT NULL, + "project_id" text NOT NULL, + "name" text NOT NULL, + "order" integer NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "projects" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "number" integer NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "creator_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "scheduled_workflows" ( + "id" text PRIMARY KEY NOT NULL, + "workflow_id" text NOT NULL, + "cron_expression" text NOT NULL, + "timezone" text DEFAULT 'UTC', + "is_enabled" boolean DEFAULT true, + "last_run_at" timestamp, + "next_run_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_artifacts" ( + "id" text PRIMARY KEY NOT NULL, + "run_id" text NOT NULL, + "job_id" text, + "name" text NOT NULL, + "size_bytes" integer NOT NULL, + "storage_path" text NOT NULL, + "mime_type" text, + "download_count" integer DEFAULT 0, + "expires_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_jobs" ( + "id" text PRIMARY KEY NOT NULL, + "run_id" text NOT NULL, + "name" text NOT NULL, + "status" text DEFAULT 'queued' NOT NULL, + "conclusion" text, + "runner_id" text, + "runner_name" text, + "runner_group_id" text, + "runner_group_name" text, + "container_id" text, + "container_image" text, + "needs" text, + "environment" text, + "environment_url" text, + "matrix" text, + "started_at" timestamp, + "completed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_logs" ( + "id" text PRIMARY KEY NOT NULL, + "job_id" text NOT NULL, + "step_id" text, + "log_level" text DEFAULT 'info' NOT NULL, + "message" text NOT NULL, + "timestamp" text NOT NULL, + "line_number" integer, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_runs" ( + "id" text PRIMARY KEY NOT NULL, + "workflow_id" text NOT NULL, + "repository_id" text NOT NULL, + "run_number" integer NOT NULL, + "run_attempt" integer DEFAULT 1 NOT NULL, + "name" text NOT NULL, + "display_title" text, + "status" text DEFAULT 'queued' NOT NULL, + "conclusion" text, + "event" text NOT NULL, + "head_branch" text, + "head_sha" text NOT NULL, + "base_branch" text, + "base_sha" text, + "pull_request_id" text, + "triggered_by_id" text, + "started_at" timestamp, + "completed_at" timestamp, + "workflow_config" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_secrets" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "encrypted_value" text NOT NULL, + "environment" text, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_steps" ( + "id" text PRIMARY KEY NOT NULL, + "job_id" text NOT NULL, + "number" integer NOT NULL, + "name" text NOT NULL, + "status" text DEFAULT 'queued' NOT NULL, + "conclusion" text, + "uses" text, + "run" text, + "shell" text, + "working_directory" text, + "env" text, + "with" text, + "if" text, + "continue_on_error" boolean DEFAULT false, + "timeout_minutes" integer, + "started_at" timestamp, + "completed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_variables" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "value" text NOT NULL, + "environment" text, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflows" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "path" text NOT NULL, + "state" text DEFAULT 'active' NOT NULL, + "badge_url" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "wiki_pages" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "slug" text NOT NULL, + "title" text NOT NULL, + "content" text NOT NULL, + "format" text DEFAULT 'markdown' NOT NULL, + "parent_id" text, + "order" integer DEFAULT 0, + "last_editor_id" text, + "view_count" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "wiki_revisions" ( + "id" text PRIMARY KEY NOT NULL, + "page_id" text NOT NULL, + "content" text NOT NULL, + "message" text, + "author_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "organization_members" ( + "organization_id" text NOT NULL, + "user_id" text NOT NULL, + "role" text DEFAULT 'member' NOT NULL, + "custom_role_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "organization_members_organization_id_user_id_pk" PRIMARY KEY("organization_id","user_id") +); +--> statement-breakpoint +CREATE TABLE "organizations" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "display_name" text, + "description" text, + "email" text, + "location" text, + "website" text, + "avatar_url" text, + "is_verified" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "organizations_name_unique" UNIQUE("name") +); +--> statement-breakpoint +CREATE TABLE "custom_roles" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "permissions" jsonb DEFAULT '[]'::jsonb NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "team_members" ( + "team_id" text NOT NULL, + "user_id" text NOT NULL, + "role" text DEFAULT 'member' NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "team_members_team_id_user_id_pk" PRIMARY KEY("team_id","user_id") +); +--> statement-breakpoint +CREATE TABLE "team_repositories" ( + "team_id" text NOT NULL, + "repository_id" text NOT NULL, + "permission" text DEFAULT 'read' NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "team_repositories_team_id_repository_id_pk" PRIMARY KEY("team_id","repository_id") +); +--> statement-breakpoint +CREATE TABLE "teams" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "name" text NOT NULL, + "slug" text NOT NULL, + "description" text, + "privacy" text DEFAULT 'visible', + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "activities" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "repository_id" text, + "type" text NOT NULL, + "action" text NOT NULL, + "ref_type" text, + "ref_name" text, + "target_type" text, + "target_id" text, + "payload" text, + "is_public" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "audit_logs" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text, + "repository_id" text, + "organization_id" text, + "action" text NOT NULL, + "actor_type" text DEFAULT 'user' NOT NULL, + "actor_id" text, + "actor_ip" text, + "actor_user_agent" text, + "target_type" text, + "target_id" text, + "target_name" text, + "data" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "notifications" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "repository_id" text, + "type" text NOT NULL, + "title" text NOT NULL, + "body" text, + "url" text, + "actor_id" text, + "subject_type" text, + "subject_id" text, + "reason" text NOT NULL, + "is_read" boolean DEFAULT false, + "read_at" timestamp, + "is_archived" boolean DEFAULT false, + "archived_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "security_scans" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "status" text DEFAULT 'queued' NOT NULL, + "started_at" timestamp, + "completed_at" timestamp, + "critical_count" integer DEFAULT 0, + "high_count" integer DEFAULT 0, + "medium_count" integer DEFAULT 0, + "low_count" integer DEFAULT 0, + "unknown_count" integer DEFAULT 0, + "logs" text +); +--> statement-breakpoint +CREATE TABLE "security_vulnerabilities" ( + "id" text PRIMARY KEY NOT NULL, + "scan_id" text NOT NULL, + "vulnerability_id" text NOT NULL, + "pkg_name" text NOT NULL, + "installed_version" text, + "fixed_version" text, + "severity" text NOT NULL, + "title" text, + "description" text, + "target" text, + "class" text +); +--> statement-breakpoint +CREATE TABLE "security_policies" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "enforcement_mode" text DEFAULT 'warn' NOT NULL, + "secret_blocked_types" text DEFAULT '[]' NOT NULL, + "secret_min_severity" text DEFAULT 'HIGH' NOT NULL, + "license_allowed_types" text DEFAULT '["permissive"]' NOT NULL, + "license_blocked_licenses" text DEFAULT '[]' NOT NULL, + "is_enabled" boolean DEFAULT true NOT NULL, + "updated_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pipeline_runners" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text, + "token" text NOT NULL, + "name" text NOT NULL, + "os" text, + "arch" text, + "version" text, + "status" text DEFAULT 'offline' NOT NULL, + "last_seen_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_stack_entries" ( + "id" text PRIMARY KEY NOT NULL, + "stack_id" text NOT NULL, + "pull_request_id" text NOT NULL, + "stack_order" integer NOT NULL, + "parent_pr_id" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_stacks" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text, + "base_branch" text NOT NULL, + "status" text DEFAULT 'active' NOT NULL, + "created_by_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "merge_queue" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "pull_request_id" text NOT NULL, + "stack_id" text, + "status" text DEFAULT 'pending' NOT NULL, + "priority" integer DEFAULT 0, + "position" integer DEFAULT 0, + "ci_status" text DEFAULT 'pending', + "merge_method" text DEFAULT 'merge', + "delete_on_merge" boolean DEFAULT true, + "execution_branch" text, + "attempt_count" integer DEFAULT 0, + "last_attempt_at" timestamp, + "added_by_id" text, + "added_at" timestamp DEFAULT now() NOT NULL, + "queued_at" timestamp DEFAULT now() NOT NULL, + "started_at" timestamp, + "completed_at" timestamp, + "failure_reason" text +); +--> statement-breakpoint +CREATE TABLE "merge_queue_speculative_runs" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "pull_request_ids" text NOT NULL, + "branch_name" text NOT NULL, + "base_branch" text NOT NULL, + "status" text DEFAULT 'pending' NOT NULL, + "ci_url" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "started_at" timestamp, + "completed_at" timestamp, + "failure_reason" text, + "commit_sha" text +); +--> statement-breakpoint +CREATE TABLE "ai_review_suggestions" ( + "id" text PRIMARY KEY NOT NULL, + "ai_review_id" text NOT NULL, + "path" text NOT NULL, + "line" integer, + "end_line" integer, + "severity" text NOT NULL, + "type" text NOT NULL, + "category" text, + "title" text NOT NULL, + "message" text NOT NULL, + "suggested_fix" text, + "explanation" text, + "is_applied" boolean DEFAULT false, + "is_dismissed" boolean DEFAULT false, + "applied_at" timestamp, + "applied_by_id" text, + "dismissed_at" timestamp, + "dismissed_by_id" text, + "dismiss_reason" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ai_reviews" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "status" text DEFAULT 'pending' NOT NULL, + "model" text NOT NULL, + "provider" text NOT NULL, + "stack_context" text, + "includes_stack_context" boolean DEFAULT false, + "summary" text, + "overall_severity" text, + "suggestions_count" integer DEFAULT 0, + "tokens_used" integer, + "prompt_tokens" integer, + "completion_tokens" integer, + "cost_cents" integer, + "raw_response" text, + "triggered_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "started_at" timestamp, + "completed_at" timestamp, + "error_message" text +); +--> statement-breakpoint +CREATE TABLE "pr_metrics" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "repository_id" text NOT NULL, + "author_id" text NOT NULL, + "time_to_first_review" integer, + "time_to_approval" integer, + "time_to_merge" integer, + "total_cycle_time" integer, + "review_rounds" integer DEFAULT 1, + "reviewers_count" integer DEFAULT 0, + "comments_count" integer DEFAULT 0, + "changes_requested_count" integer DEFAULT 0, + "lines_added" integer DEFAULT 0, + "lines_removed" integer DEFAULT 0, + "files_changed" integer DEFAULT 0, + "commits" integer DEFAULT 1, + "is_stacked" boolean DEFAULT false, + "stack_position" integer, + "pr_created_at" timestamp, + "first_review_at" timestamp, + "approved_at" timestamp, + "merged_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "repo_metrics" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "week_of" text NOT NULL, + "prs_opened" integer DEFAULT 0, + "prs_merged" integer DEFAULT 0, + "prs_closed" integer DEFAULT 0, + "avg_time_to_first_review" integer, + "avg_time_to_merge" integer, + "avg_review_rounds" integer, + "avg_lines_changed" integer, + "avg_files_changed" integer, + "stacked_prs" integer DEFAULT 0, + "stacked_prs_percentage" integer DEFAULT 0, + "active_authors" integer DEFAULT 0, + "active_reviewers" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "review_metrics" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "repository_id" text, + "week_of" text NOT NULL, + "prs_authored" integer DEFAULT 0, + "prs_authored_merged" integer DEFAULT 0, + "avg_time_to_merge_authored" integer, + "lines_authored_added" integer DEFAULT 0, + "lines_authored_removed" integer DEFAULT 0, + "prs_reviewed" integer DEFAULT 0, + "avg_time_to_review" integer, + "comments_given" integer DEFAULT 0, + "approvals_given" integer DEFAULT 0, + "changes_requested_given" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "slack_channel_mappings" ( + "id" text PRIMARY KEY NOT NULL, + "workspace_id" text NOT NULL, + "repository_id" text NOT NULL, + "channel_id" text NOT NULL, + "channel_name" text, + "notify_on" text DEFAULT '["pr_created","pr_merged","review_requested","ci_failed"]', + "notify_branches" text, + "notify_authors" text, + "is_active" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "slack_user_mappings" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "workspace_id" text NOT NULL, + "slack_user_id" text NOT NULL, + "slack_username" text, + "dm_preferences" text DEFAULT '{"review_requested":true,"pr_approved":true,"ci_failed":true}', + "dnd_enabled" boolean DEFAULT false, + "dnd_start" text, + "dnd_end" text, + "dnd_timezone" text, + "is_active" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "slack_workspaces" ( + "id" text PRIMARY KEY NOT NULL, + "team_id" text NOT NULL, + "team_name" text, + "team_domain" text, + "access_token" text NOT NULL, + "bot_user_id" text, + "bot_access_token" text, + "scopes" text, + "installed_by_id" text NOT NULL, + "installed_at" timestamp DEFAULT now() NOT NULL, + "is_active" boolean DEFAULT true, + "last_used_at" timestamp +); +--> statement-breakpoint +CREATE TABLE "webhook_deliveries" ( + "id" text PRIMARY KEY NOT NULL, + "webhook_id" text NOT NULL, + "event" text NOT NULL, + "payload" text NOT NULL, + "status" text NOT NULL, + "response_code" integer, + "response_body" text, + "duration_ms" integer, + "error" text, + "request_headers" text, + "response_headers" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "webhooks" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "provider" text DEFAULT 'generic', + "name" text, + "url" text NOT NULL, + "secret" text, + "events" text NOT NULL, + "active" boolean DEFAULT true NOT NULL, + "enabled" boolean DEFAULT true NOT NULL, + "content_type" text DEFAULT 'json' NOT NULL, + "delivery_count" integer DEFAULT 0, + "last_delivery_status" text, + "last_delivery_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "created_by_id" text +); +--> statement-breakpoint +CREATE TABLE "branch_protection" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "pattern" text NOT NULL, + "active" boolean DEFAULT true, + "requires_pr" boolean DEFAULT false, + "required_approvals" integer DEFAULT 1, + "dismiss_stale_reviews" boolean DEFAULT false, + "require_code_owner_reviews" boolean DEFAULT false, + "allow_force_pushes" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "created_by_id" text +); +--> statement-breakpoint +CREATE TABLE "system_config" ( + "key" text PRIMARY KEY NOT NULL, + "value" text NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "updated_by_id" text +); +--> statement-breakpoint +CREATE TABLE "inbox_sections" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "name" text NOT NULL, + "icon" text, + "color" text, + "filters" text, + "position" integer DEFAULT 0 NOT NULL, + "is_default" boolean DEFAULT false, + "is_collapsed" boolean DEFAULT false, + "show_count" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "shared_inbox_sections" ( + "id" text PRIMARY KEY NOT NULL, + "section_id" text NOT NULL, + "shared_with_user_id" text, + "shared_with_team_id" text, + "permission" text DEFAULT 'view' NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "automation_executions" ( + "id" text PRIMARY KEY NOT NULL, + "rule_id" text NOT NULL, + "triggered_by_id" text, + "triggered_by_type" text, + "trigger_event" text NOT NULL, + "conditions_matched" boolean, + "actions_executed" text, + "status" text DEFAULT 'pending' NOT NULL, + "error_message" text, + "duration_ms" integer, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "automation_rules" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text, + "organization_id" text, + "created_by_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "trigger" text NOT NULL, + "conditions" text, + "actions" text NOT NULL, + "is_enabled" boolean DEFAULT true, + "priority" integer DEFAULT 0, + "run_count" integer DEFAULT 0, + "last_run_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ai_codebase_context" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "context_type" text NOT NULL, + "name" text NOT NULL, + "description" text, + "examples" text, + "anti_patterns" text, + "is_auto_detected" boolean DEFAULT false, + "confidence" integer, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ai_review_rule_templates" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "description" text, + "rule_type" text NOT NULL, + "ai_prompt" text, + "regex_pattern" text, + "severity" text NOT NULL, + "category" text, + "file_globs" text, + "languages" text, + "is_built_in" boolean DEFAULT false, + "usage_count" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ai_review_rules" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text, + "organization_id" text, + "created_by_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "rule_type" text DEFAULT 'ai_prompt' NOT NULL, + "ai_prompt" text, + "regex_pattern" text, + "severity" text DEFAULT 'info' NOT NULL, + "category" text, + "file_globs" text, + "exclude_globs" text, + "languages" text, + "is_enabled" boolean DEFAULT true, + "is_auto_fix" boolean DEFAULT false, + "priority" integer DEFAULT 0, + "match_count" integer DEFAULT 0, + "last_match_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "email_digest_settings" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "digest_type" text DEFAULT 'none' NOT NULL, + "digest_time" text DEFAULT '09:00', + "digest_day" integer DEFAULT 1, + "timezone" text DEFAULT 'UTC' NOT NULL, + "last_sent_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "notification_preferences" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "event_type" text NOT NULL, + "email_enabled" boolean DEFAULT true, + "slack_enabled" boolean DEFAULT false, + "in_app_enabled" boolean DEFAULT true, + "browser_push_enabled" boolean DEFAULT false, + "repository_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "notification_quiet_hours" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "is_enabled" boolean DEFAULT false, + "start_time" text DEFAULT '22:00' NOT NULL, + "end_time" text DEFAULT '08:00' NOT NULL, + "timezone" text DEFAULT 'UTC' NOT NULL, + "days_of_week" text DEFAULT '0,1,2,3,4,5,6', + "allow_urgent" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "push_subscriptions" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "endpoint" text NOT NULL, + "p256dh_key" text NOT NULL, + "auth_key" text NOT NULL, + "user_agent" text, + "is_active" boolean DEFAULT true, + "last_used_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "sso_configs" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "type" text DEFAULT 'oidc' NOT NULL, + "issuer" text NOT NULL, + "client_id" text NOT NULL, + "client_secret" text NOT NULL, + "scopes" text DEFAULT 'openid,profile,email', + "authorization_url" text, + "token_url" text, + "userinfo_url" text, + "jwks_uri" text, + "enabled" boolean DEFAULT false, + "auto_create_users" boolean DEFAULT true, + "allowed_domains" text, + "default_role" text DEFAULT 'member', + "organization_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "created_by_id" text +); +--> statement-breakpoint +CREATE TABLE "repository_path_permissions" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "path_pattern" text NOT NULL, + "user_id" text, + "team_id" text, + "permission" text DEFAULT 'write' NOT NULL, + "require_approval" text DEFAULT 'false', + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_state_definitions" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "display_name" text NOT NULL, + "description" text, + "color" text DEFAULT '#6B7280' NOT NULL, + "icon" text, + "order" integer DEFAULT 0 NOT NULL, + "is_default" boolean DEFAULT false, + "is_final" boolean DEFAULT false, + "allow_merge" boolean DEFAULT false, + "require_code_owner" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_state_reviewers" ( + "id" text PRIMARY KEY NOT NULL, + "state_definition_id" text NOT NULL, + "user_id" text, + "team_id" text, + "required_count" integer DEFAULT 1, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_state_transitions" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "from_state" text, + "to_state" text NOT NULL, + "changed_by_id" text NOT NULL, + "reason" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_statuses" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "color" text DEFAULT '#808080' NOT NULL, + "order" integer DEFAULT 0 NOT NULL, + "type" text DEFAULT 'open' NOT NULL, + "is_default" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "custom_field_definitions" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "type" text NOT NULL, + "options" json, + "required" boolean DEFAULT false, + "order" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_custom_field_values" ( + "id" text PRIMARY KEY NOT NULL, + "issue_id" text NOT NULL, + "field_id" text NOT NULL, + "value" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "auto_merge_rules" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "match_labels" text, + "required_labels" text, + "required_checks" text, + "min_approvals" integer DEFAULT 0, + "require_code_owner" boolean DEFAULT false, + "allow_draft" boolean DEFAULT false, + "min_time_in_queue_minutes" integer DEFAULT 0, + "merge_method" text, + "is_enabled" boolean DEFAULT true, + "match_count" integer DEFAULT 0, + "last_matched_at" timestamp, + "last_mismatch_reason" text, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "external_ci_integrations" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "token_hash" text NOT NULL, + "name" text, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "last_used_at" timestamp +); +--> statement-breakpoint +CREATE TABLE "review_requirements" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "min_approvals" integer DEFAULT 1, + "require_code_owner" boolean DEFAULT false, + "require_team_lead" boolean DEFAULT false, + "dismiss_stale_reviews" boolean DEFAULT false, + "require_rereview_on_push" boolean DEFAULT false, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "reviewer_rules" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "rule_type" text NOT NULL, + "target_id" text, + "count" integer, + "path_pattern" text, + "is_required" boolean DEFAULT true, + "is_enabled" boolean DEFAULT true, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "api_change_detections" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "change_type" text NOT NULL, + "path" text NOT NULL, + "method" text, + "breaking" boolean DEFAULT false, + "details" text NOT NULL, + "affected_files" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "breaking_changes" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "change_type" text NOT NULL, + "severity" text NOT NULL, + "description" text NOT NULL, + "affected_files" jsonb, + "suggested_action" text, + "acknowledged" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "change_set_items" ( + "id" text PRIMARY KEY NOT NULL, + "change_set_id" text NOT NULL, + "pull_request_id" text, + "repository_id" text NOT NULL, + "order" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "change_sets" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "description" text, + "created_by_id" text NOT NULL, + "status" text DEFAULT 'draft' NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "chat_integrations" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text, + "repository_id" text, + "provider" text NOT NULL, + "name" text NOT NULL, + "webhook_url" text, + "api_token" text, + "channel_id" text, + "is_enabled" boolean DEFAULT true, + "events" jsonb DEFAULT '[]'::jsonb, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "cloud_configs" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text, + "organization_id" text, + "provider" text NOT NULL, + "name" text NOT NULL, + "region" text, + "credentials" jsonb, + "settings" jsonb, + "is_enabled" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "code_quality_configs" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "provider" text NOT NULL, + "project_key" text, + "api_token" text, + "server_url" text, + "webhook_secret" text, + "is_enabled" boolean DEFAULT true, + "report_on_pr" boolean DEFAULT true, + "block_on_fail" boolean DEFAULT false, + "min_coverage" real, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "code_quality_issues" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "pull_request_id" text, + "commit_sha" text NOT NULL, + "provider" text NOT NULL, + "issue_type" text NOT NULL, + "severity" text NOT NULL, + "message" text NOT NULL, + "file" text, + "line" integer, + "rule" text, + "effort" text, + "status" text DEFAULT 'open', + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "coverage_reports" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "pull_request_id" text, + "commit_sha" text NOT NULL, + "provider" text NOT NULL, + "coverage" real NOT NULL, + "lines_covered" integer, + "lines_total" integer, + "branch_coverage" real, + "delta" real, + "status" text NOT NULL, + "report_url" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "cross_repo_issue_links" ( + "id" text PRIMARY KEY NOT NULL, + "source_issue_id" text NOT NULL, + "target_issue_id" text NOT NULL, + "link_type" text NOT NULL, + "created_by_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "custom_dashboards" ( + "id" text PRIMARY KEY NOT NULL, + "user_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "is_public" boolean DEFAULT false, + "layout" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "dashboard_widgets" ( + "id" text PRIMARY KEY NOT NULL, + "dashboard_id" text NOT NULL, + "widget_type" text NOT NULL, + "title" text NOT NULL, + "config" jsonb, + "position" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "cloud_deployments" ( + "id" text PRIMARY KEY NOT NULL, + "config_id" text NOT NULL, + "pull_request_id" text, + "commit_sha" text NOT NULL, + "environment" text NOT NULL, + "status" text NOT NULL, + "url" text, + "logs" text, + "started_at" timestamp, + "completed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "email_settings" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text, + "smtp_host" text, + "smtp_port" text, + "smtp_user" text, + "smtp_pass" text, + "from_address" text, + "from_name" text, + "is_enabled" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "external_builds" ( + "id" text PRIMARY KEY NOT NULL, + "config_id" text NOT NULL, + "pull_request_id" text, + "external_build_id" text NOT NULL, + "build_number" text, + "status" text NOT NULL, + "url" text, + "started_at" timestamp, + "completed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "external_ci_configs" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "provider" text NOT NULL, + "name" text NOT NULL, + "base_url" text NOT NULL, + "api_token" text, + "project_id" text, + "webhook_secret" text, + "is_enabled" boolean DEFAULT true, + "sync_status" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "file_approvals" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "path" text NOT NULL, + "approved_by_id" text NOT NULL, + "approved_at" timestamp DEFAULT now() NOT NULL, + "commit_sha" text NOT NULL, + "comment" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "file_hotspots" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "file_path" text NOT NULL, + "change_count" integer DEFAULT 0, + "bug_count" integer DEFAULT 0, + "review_count" integer DEFAULT 0, + "complexity_score" real, + "last_modified" timestamp, + "calculated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ip_allow_lists" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "name" text NOT NULL, + "cidr_block" text NOT NULL, + "description" text, + "is_enabled" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "created_by_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_tracker_configs" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "provider" text NOT NULL, + "name" text NOT NULL, + "api_url" text, + "api_token" text, + "project_key" text, + "webhook_secret" text, + "is_enabled" boolean DEFAULT true, + "sync_to_external" boolean DEFAULT true, + "sync_from_external" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "issue_tracker_links" ( + "id" text PRIMARY KEY NOT NULL, + "config_id" text NOT NULL, + "local_issue_id" text, + "external_id" text NOT NULL, + "external_key" text, + "external_url" text, + "last_sync_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "license_scans" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "commit_sha" text NOT NULL, + "package_name" text NOT NULL, + "package_version" text, + "license" text NOT NULL, + "license_type" text NOT NULL, + "is_compliant" boolean NOT NULL, + "policy_violation" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "merge_gates" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "description" text, + "gate_type" text NOT NULL, + "config" text, + "condition_script" text, + "is_enabled" boolean DEFAULT true, + "order" integer DEFAULT 0, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "metric_snapshots" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text, + "organization_id" text, + "metric_type" text NOT NULL, + "value" real NOT NULL, + "dimensions" jsonb, + "timestamp" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "migration_detections" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "migration_type" text NOT NULL, + "tool" text, + "files" jsonb, + "is_reversible" boolean, + "requires_downtime" boolean, + "notes" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_issue_links" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "issue_id" text NOT NULL, + "link_type" text NOT NULL, + "created_by_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "rate_limit_logs" ( + "id" text PRIMARY KEY NOT NULL, + "rule_id" text NOT NULL, + "key" text NOT NULL, + "request_count" integer NOT NULL, + "window_start" timestamp NOT NULL, + "blocked" boolean DEFAULT false +); +--> statement-breakpoint +CREATE TABLE "rate_limit_rules" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "path" text NOT NULL, + "method" text, + "window_ms" integer NOT NULL, + "max_requests" integer NOT NULL, + "key_type" text DEFAULT 'ip', + "is_enabled" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "repository_workflows" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "path" text NOT NULL, + "content" text NOT NULL, + "template_id" text, + "is_enabled" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "required_status_checks" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "branch" text NOT NULL, + "check_name" text NOT NULL, + "is_required" boolean DEFAULT true, + "strict_mode" boolean DEFAULT true, + "path_filter" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "review_templates" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "organization_id" text, + "name" text NOT NULL, + "description" text, + "category" text, + "content" text NOT NULL, + "is_default" boolean DEFAULT false NOT NULL, + "created_by_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "saml_configs" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "entity_id" text NOT NULL, + "sso_url" text NOT NULL, + "certificate" text NOT NULL, + "signature_algorithm" text DEFAULT 'RSA-SHA256', + "digest_algorithm" text DEFAULT 'SHA256', + "is_enabled" boolean DEFAULT true, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "secret_scan_results" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "commit_sha" text NOT NULL, + "secret_type" text NOT NULL, + "file" text NOT NULL, + "line" integer, + "snippet" text, + "severity" text NOT NULL, + "status" text DEFAULT 'open', + "resolved_at" timestamp, + "resolved_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_states" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "name" text NOT NULL, + "category" text NOT NULL, + "color" text DEFAULT '#6b7280', + "icon" text, + "display_order" integer DEFAULT 0, + "is_default" boolean DEFAULT false, + "is_closed_state" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_templates" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "description" text, + "category" text NOT NULL, + "language" text, + "content" text NOT NULL, + "is_official" boolean DEFAULT false, + "is_public" boolean DEFAULT true, + "created_by_id" text, + "downloads" text DEFAULT '0', + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_transitions" ( + "id" text PRIMARY KEY NOT NULL, + "repository_id" text NOT NULL, + "from_state_id" text, + "to_state_id" text NOT NULL, + "requires_comment" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "package_versions" ( + "id" text PRIMARY KEY NOT NULL, + "package_id" text NOT NULL, + "version" text NOT NULL, + "digest" text, + "size_bytes" integer, + "storage_path" text NOT NULL, + "metadata" jsonb, + "tags" jsonb DEFAULT '[]'::jsonb, + "git_ref" text, + "published_by_id" text, + "download_count" integer DEFAULT 0 NOT NULL, + "yanked" boolean DEFAULT false, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "packages" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text, + "repository_id" text, + "type" text NOT NULL, + "name" text NOT NULL, + "description" text, + "visibility" text DEFAULT 'private' NOT NULL, + "download_count" integer DEFAULT 0 NOT NULL, + "created_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ai_inline_comments" ( + "id" text PRIMARY KEY NOT NULL, + "analysis_id" text NOT NULL, + "pull_request_id" text NOT NULL, + "file_path" text NOT NULL, + "line" integer NOT NULL, + "end_line" integer, + "side" text DEFAULT 'right', + "severity" text NOT NULL, + "type" text NOT NULL, + "category" text, + "title" text NOT NULL, + "message" text NOT NULL, + "suggested_fix" text, + "explanation" text, + "confidence" integer, + "is_resolved" integer DEFAULT 0, + "is_applied" integer DEFAULT 0, + "applied_at" timestamp, + "dismissed_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "ai_review_threads" ( + "id" text PRIMARY KEY NOT NULL, + "analysis_id" text NOT NULL, + "pull_request_id" text NOT NULL, + "file_path" text, + "line" integer, + "messages" jsonb NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "file_summaries" ( + "id" text PRIMARY KEY NOT NULL, + "analysis_id" text NOT NULL, + "file_path" text NOT NULL, + "language" text, + "summary" text, + "risk_level" text, + "additions" integer, + "deletions" integer, + "complexity_score" integer, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "pr_review_analyses" ( + "id" text PRIMARY KEY NOT NULL, + "pull_request_id" text NOT NULL, + "repository_id" text NOT NULL, + "status" text DEFAULT 'pending' NOT NULL, + "complexity_data" jsonb, + "dependency_graph" jsonb, + "architecture_impact" jsonb, + "change_groups" jsonb, + "blast_radius" jsonb, + "health_score" integer, + "health_grade" text, + "summaries" jsonb, + "base_sha" text, + "head_sha" text, + "files_analyzed" integer, + "chunks_processed" integer, + "total_additions" integer, + "total_deletions" integer, + "model" text, + "provider" text, + "tokens_used" integer, + "cost_cents" integer, + "raw_ai_response" jsonb, + "triggered_by_id" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "started_at" timestamp, + "completed_at" timestamp, + "error_message" text +); +--> statement-breakpoint +ALTER TABLE "email_verification_tokens" ADD CONSTRAINT "email_verification_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "gpg_keys" ADD CONSTRAINT "gpg_keys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "oauth_accounts" ADD CONSTRAINT "oauth_accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "personal_access_tokens" ADD CONSTRAINT "personal_access_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ssh_keys" ADD CONSTRAINT "ssh_keys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_followers" ADD CONSTRAINT "user_followers_follower_id_users_id_fk" FOREIGN KEY ("follower_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "user_followers" ADD CONSTRAINT "user_followers_followee_id_users_id_fk" FOREIGN KEY ("followee_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "deploy_keys" ADD CONSTRAINT "deploy_keys_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "branches" ADD CONSTRAINT "branches_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "commits" ADD CONSTRAINT "commits_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "commits" ADD CONSTRAINT "commits_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "releases" ADD CONSTRAINT "releases_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "releases" ADD CONSTRAINT "releases_tag_id_tags_id_fk" FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "releases" ADD CONSTRAINT "releases_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repositories" ADD CONSTRAINT "repositories_owner_id_users_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repositories" ADD CONSTRAINT "repositories_forked_from_id_repositories_id_fk" FOREIGN KEY ("forked_from_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_collaborators" ADD CONSTRAINT "repository_collaborators_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_collaborators" ADD CONSTRAINT "repository_collaborators_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_collaborators" ADD CONSTRAINT "repository_collaborators_added_by_id_users_id_fk" FOREIGN KEY ("added_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_stars" ADD CONSTRAINT "repository_stars_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_stars" ADD CONSTRAINT "repository_stars_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_watchers" ADD CONSTRAINT "repository_watchers_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_watchers" ADD CONSTRAINT "repository_watchers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "tags" ADD CONSTRAINT "tags_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_assignees" ADD CONSTRAINT "issue_assignees_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_assignees" ADD CONSTRAINT "issue_assignees_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_comments" ADD CONSTRAINT "issue_comments_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_comments" ADD CONSTRAINT "issue_comments_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_labels" ADD CONSTRAINT "issue_labels_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_labels" ADD CONSTRAINT "issue_labels_label_id_labels_id_fk" FOREIGN KEY ("label_id") REFERENCES "public"."labels"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_subscribers" ADD CONSTRAINT "issue_subscribers_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_subscribers" ADD CONSTRAINT "issue_subscribers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issues" ADD CONSTRAINT "issues_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issues" ADD CONSTRAINT "issues_status_id_issue_statuses_id_fk" FOREIGN KEY ("status_id") REFERENCES "public"."issue_statuses"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issues" ADD CONSTRAINT "issues_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issues" ADD CONSTRAINT "issues_assignee_id_users_id_fk" FOREIGN KEY ("assignee_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issues" ADD CONSTRAINT "issues_closed_by_id_users_id_fk" FOREIGN KEY ("closed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "labels" ADD CONSTRAINT "labels_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "milestones" ADD CONSTRAINT "milestones_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_assignees" ADD CONSTRAINT "pull_request_assignees_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_assignees" ADD CONSTRAINT "pull_request_assignees_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_checks" ADD CONSTRAINT "pull_request_checks_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_review_id_pull_request_reviews_id_fk" FOREIGN KEY ("review_id") REFERENCES "public"."pull_request_reviews"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_in_reply_to_id_pull_request_comments_id_fk" FOREIGN KEY ("in_reply_to_id") REFERENCES "public"."pull_request_comments"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_resolved_by_id_users_id_fk" FOREIGN KEY ("resolved_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_suggestion_applied_by_id_users_id_fk" FOREIGN KEY ("suggestion_applied_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_labels" ADD CONSTRAINT "pull_request_labels_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_labels" ADD CONSTRAINT "pull_request_labels_label_id_labels_id_fk" FOREIGN KEY ("label_id") REFERENCES "public"."labels"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_reviewers" ADD CONSTRAINT "pull_request_reviewers_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_reviewers" ADD CONSTRAINT "pull_request_reviewers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_reviews" ADD CONSTRAINT "pull_request_reviews_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_reviews" ADD CONSTRAINT "pull_request_reviews_reviewer_id_users_id_fk" FOREIGN KEY ("reviewer_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_request_reviews" ADD CONSTRAINT "pull_request_reviews_dismissed_by_id_users_id_fk" FOREIGN KEY ("dismissed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_assignee_id_users_id_fk" FOREIGN KEY ("assignee_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_head_repository_id_repositories_id_fk" FOREIGN KEY ("head_repository_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_merged_by_id_users_id_fk" FOREIGN KEY ("merged_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_auto_merge_enabled_by_id_users_id_fk" FOREIGN KEY ("auto_merge_enabled_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_state_id_pr_state_definitions_id_fk" FOREIGN KEY ("state_id") REFERENCES "public"."pr_state_definitions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_closed_by_id_users_id_fk" FOREIGN KEY ("closed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project_cards" ADD CONSTRAINT "project_cards_column_id_project_columns_id_fk" FOREIGN KEY ("column_id") REFERENCES "public"."project_columns"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project_cards" ADD CONSTRAINT "project_cards_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project_columns" ADD CONSTRAINT "project_columns_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "projects" ADD CONSTRAINT "projects_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scheduled_workflows" ADD CONSTRAINT "scheduled_workflows_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_artifacts" ADD CONSTRAINT "workflow_artifacts_run_id_workflow_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."workflow_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_artifacts" ADD CONSTRAINT "workflow_artifacts_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_jobs" ADD CONSTRAINT "workflow_jobs_run_id_workflow_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."workflow_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_logs" ADD CONSTRAINT "workflow_logs_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_logs" ADD CONSTRAINT "workflow_logs_step_id_workflow_steps_id_fk" FOREIGN KEY ("step_id") REFERENCES "public"."workflow_steps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_triggered_by_id_users_id_fk" FOREIGN KEY ("triggered_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_secrets" ADD CONSTRAINT "workflow_secrets_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_secrets" ADD CONSTRAINT "workflow_secrets_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_steps" ADD CONSTRAINT "workflow_steps_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_variables" ADD CONSTRAINT "workflow_variables_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_variables" ADD CONSTRAINT "workflow_variables_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflows" ADD CONSTRAINT "workflows_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "wiki_pages" ADD CONSTRAINT "wiki_pages_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "wiki_pages" ADD CONSTRAINT "wiki_pages_parent_id_wiki_pages_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."wiki_pages"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "wiki_pages" ADD CONSTRAINT "wiki_pages_last_editor_id_users_id_fk" FOREIGN KEY ("last_editor_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "wiki_revisions" ADD CONSTRAINT "wiki_revisions_page_id_wiki_pages_id_fk" FOREIGN KEY ("page_id") REFERENCES "public"."wiki_pages"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "wiki_revisions" ADD CONSTRAINT "wiki_revisions_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "custom_roles" ADD CONSTRAINT "custom_roles_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "team_members" ADD CONSTRAINT "team_members_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "team_members" ADD CONSTRAINT "team_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "team_repositories" ADD CONSTRAINT "team_repositories_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "team_repositories" ADD CONSTRAINT "team_repositories_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "teams" ADD CONSTRAINT "teams_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "activities" ADD CONSTRAINT "activities_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "activities" ADD CONSTRAINT "activities_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notifications" ADD CONSTRAINT "notifications_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notifications" ADD CONSTRAINT "notifications_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notifications" ADD CONSTRAINT "notifications_actor_id_users_id_fk" FOREIGN KEY ("actor_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_scans" ADD CONSTRAINT "security_scans_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_vulnerabilities" ADD CONSTRAINT "security_vulnerabilities_scan_id_security_scans_id_fk" FOREIGN KEY ("scan_id") REFERENCES "public"."security_scans"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_policies" ADD CONSTRAINT "security_policies_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_policies" ADD CONSTRAINT "security_policies_updated_by_id_users_id_fk" FOREIGN KEY ("updated_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pipeline_runners" ADD CONSTRAINT "pipeline_runners_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_stack_entries" ADD CONSTRAINT "pr_stack_entries_stack_id_pr_stacks_id_fk" FOREIGN KEY ("stack_id") REFERENCES "public"."pr_stacks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_stack_entries" ADD CONSTRAINT "pr_stack_entries_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_stack_entries" ADD CONSTRAINT "pr_stack_entries_parent_pr_id_pull_requests_id_fk" FOREIGN KEY ("parent_pr_id") REFERENCES "public"."pull_requests"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_stacks" ADD CONSTRAINT "pr_stacks_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_stacks" ADD CONSTRAINT "pr_stacks_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_added_by_id_users_id_fk" FOREIGN KEY ("added_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "merge_queue_speculative_runs" ADD CONSTRAINT "merge_queue_speculative_runs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_suggestions" ADD CONSTRAINT "ai_review_suggestions_ai_review_id_ai_reviews_id_fk" FOREIGN KEY ("ai_review_id") REFERENCES "public"."ai_reviews"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_suggestions" ADD CONSTRAINT "ai_review_suggestions_applied_by_id_users_id_fk" FOREIGN KEY ("applied_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_suggestions" ADD CONSTRAINT "ai_review_suggestions_dismissed_by_id_users_id_fk" FOREIGN KEY ("dismissed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_reviews" ADD CONSTRAINT "ai_reviews_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_reviews" ADD CONSTRAINT "ai_reviews_triggered_by_id_users_id_fk" FOREIGN KEY ("triggered_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_metrics" ADD CONSTRAINT "pr_metrics_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_metrics" ADD CONSTRAINT "pr_metrics_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_metrics" ADD CONSTRAINT "pr_metrics_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repo_metrics" ADD CONSTRAINT "repo_metrics_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "review_metrics" ADD CONSTRAINT "review_metrics_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "review_metrics" ADD CONSTRAINT "review_metrics_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "slack_channel_mappings" ADD CONSTRAINT "slack_channel_mappings_workspace_id_slack_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."slack_workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "slack_channel_mappings" ADD CONSTRAINT "slack_channel_mappings_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "slack_user_mappings" ADD CONSTRAINT "slack_user_mappings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "slack_user_mappings" ADD CONSTRAINT "slack_user_mappings_workspace_id_slack_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."slack_workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "slack_workspaces" ADD CONSTRAINT "slack_workspaces_installed_by_id_users_id_fk" FOREIGN KEY ("installed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "webhook_deliveries" ADD CONSTRAINT "webhook_deliveries_webhook_id_webhooks_id_fk" FOREIGN KEY ("webhook_id") REFERENCES "public"."webhooks"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "webhooks" ADD CONSTRAINT "webhooks_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "webhooks" ADD CONSTRAINT "webhooks_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "branch_protection" ADD CONSTRAINT "branch_protection_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "branch_protection" ADD CONSTRAINT "branch_protection_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "system_config" ADD CONSTRAINT "system_config_updated_by_id_users_id_fk" FOREIGN KEY ("updated_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inbox_sections" ADD CONSTRAINT "inbox_sections_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "shared_inbox_sections" ADD CONSTRAINT "shared_inbox_sections_section_id_inbox_sections_id_fk" FOREIGN KEY ("section_id") REFERENCES "public"."inbox_sections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "shared_inbox_sections" ADD CONSTRAINT "shared_inbox_sections_shared_with_user_id_users_id_fk" FOREIGN KEY ("shared_with_user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "automation_executions" ADD CONSTRAINT "automation_executions_rule_id_automation_rules_id_fk" FOREIGN KEY ("rule_id") REFERENCES "public"."automation_rules"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "automation_rules" ADD CONSTRAINT "automation_rules_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "automation_rules" ADD CONSTRAINT "automation_rules_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_codebase_context" ADD CONSTRAINT "ai_codebase_context_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_rules" ADD CONSTRAINT "ai_review_rules_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_rules" ADD CONSTRAINT "ai_review_rules_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "email_digest_settings" ADD CONSTRAINT "email_digest_settings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notification_preferences" ADD CONSTRAINT "notification_preferences_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notification_preferences" ADD CONSTRAINT "notification_preferences_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notification_quiet_hours" ADD CONSTRAINT "notification_quiet_hours_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "push_subscriptions" ADD CONSTRAINT "push_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "sso_configs" ADD CONSTRAINT "sso_configs_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_state_definitions" ADD CONSTRAINT "pr_state_definitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_state_definition_id_pr_state_definitions_id_fk" FOREIGN KEY ("state_definition_id") REFERENCES "public"."pr_state_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_state_transitions" ADD CONSTRAINT "pr_state_transitions_changed_by_id_users_id_fk" FOREIGN KEY ("changed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_statuses" ADD CONSTRAINT "issue_statuses_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "custom_field_definitions" ADD CONSTRAINT "custom_field_definitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_custom_field_values" ADD CONSTRAINT "issue_custom_field_values_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_custom_field_values" ADD CONSTRAINT "issue_custom_field_values_field_id_custom_field_definitions_id_fk" FOREIGN KEY ("field_id") REFERENCES "public"."custom_field_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auto_merge_rules" ADD CONSTRAINT "auto_merge_rules_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "auto_merge_rules" ADD CONSTRAINT "auto_merge_rules_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "external_ci_integrations" ADD CONSTRAINT "external_ci_integrations_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "external_ci_integrations" ADD CONSTRAINT "external_ci_integrations_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "review_requirements" ADD CONSTRAINT "review_requirements_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "review_requirements" ADD CONSTRAINT "review_requirements_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "reviewer_rules" ADD CONSTRAINT "reviewer_rules_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "reviewer_rules" ADD CONSTRAINT "reviewer_rules_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "api_change_detections" ADD CONSTRAINT "api_change_detections_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "breaking_changes" ADD CONSTRAINT "breaking_changes_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_change_set_id_change_sets_id_fk" FOREIGN KEY ("change_set_id") REFERENCES "public"."change_sets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "change_sets" ADD CONSTRAINT "change_sets_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "cloud_configs" ADD CONSTRAINT "cloud_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_quality_configs" ADD CONSTRAINT "code_quality_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_quality_issues" ADD CONSTRAINT "code_quality_issues_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "coverage_reports" ADD CONSTRAINT "coverage_reports_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "cross_repo_issue_links" ADD CONSTRAINT "cross_repo_issue_links_source_issue_id_issues_id_fk" FOREIGN KEY ("source_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "cross_repo_issue_links" ADD CONSTRAINT "cross_repo_issue_links_target_issue_id_issues_id_fk" FOREIGN KEY ("target_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "custom_dashboards" ADD CONSTRAINT "custom_dashboards_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "dashboard_widgets" ADD CONSTRAINT "dashboard_widgets_dashboard_id_custom_dashboards_id_fk" FOREIGN KEY ("dashboard_id") REFERENCES "public"."custom_dashboards"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "cloud_deployments" ADD CONSTRAINT "cloud_deployments_config_id_cloud_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."cloud_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "external_builds" ADD CONSTRAINT "external_builds_config_id_external_ci_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."external_ci_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "external_ci_configs" ADD CONSTRAINT "external_ci_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "file_approvals" ADD CONSTRAINT "file_approvals_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "file_approvals" ADD CONSTRAINT "file_approvals_approved_by_id_users_id_fk" FOREIGN KEY ("approved_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "file_hotspots" ADD CONSTRAINT "file_hotspots_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_tracker_configs" ADD CONSTRAINT "issue_tracker_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_tracker_links" ADD CONSTRAINT "issue_tracker_links_config_id_issue_tracker_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."issue_tracker_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "issue_tracker_links" ADD CONSTRAINT "issue_tracker_links_local_issue_id_issues_id_fk" FOREIGN KEY ("local_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "license_scans" ADD CONSTRAINT "license_scans_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "merge_gates" ADD CONSTRAINT "merge_gates_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "metric_snapshots" ADD CONSTRAINT "metric_snapshots_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "migration_detections" ADD CONSTRAINT "migration_detections_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_issue_links" ADD CONSTRAINT "pr_issue_links_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_issue_links" ADD CONSTRAINT "pr_issue_links_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "rate_limit_logs" ADD CONSTRAINT "rate_limit_logs_rule_id_rate_limit_rules_id_fk" FOREIGN KEY ("rule_id") REFERENCES "public"."rate_limit_rules"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_workflows" ADD CONSTRAINT "repository_workflows_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "repository_workflows" ADD CONSTRAINT "repository_workflows_template_id_workflow_templates_id_fk" FOREIGN KEY ("template_id") REFERENCES "public"."workflow_templates"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "required_status_checks" ADD CONSTRAINT "required_status_checks_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "review_templates" ADD CONSTRAINT "review_templates_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "secret_scan_results" ADD CONSTRAINT "secret_scan_results_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_states" ADD CONSTRAINT "workflow_states_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_from_state_id_workflow_states_id_fk" FOREIGN KEY ("from_state_id") REFERENCES "public"."workflow_states"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_to_state_id_workflow_states_id_fk" FOREIGN KEY ("to_state_id") REFERENCES "public"."workflow_states"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "package_versions" ADD CONSTRAINT "package_versions_package_id_packages_id_fk" FOREIGN KEY ("package_id") REFERENCES "public"."packages"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "package_versions" ADD CONSTRAINT "package_versions_published_by_id_users_id_fk" FOREIGN KEY ("published_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "packages" ADD CONSTRAINT "packages_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "packages" ADD CONSTRAINT "packages_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "packages" ADD CONSTRAINT "packages_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_inline_comments" ADD CONSTRAINT "ai_inline_comments_analysis_id_pr_review_analyses_id_fk" FOREIGN KEY ("analysis_id") REFERENCES "public"."pr_review_analyses"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_inline_comments" ADD CONSTRAINT "ai_inline_comments_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_threads" ADD CONSTRAINT "ai_review_threads_analysis_id_pr_review_analyses_id_fk" FOREIGN KEY ("analysis_id") REFERENCES "public"."pr_review_analyses"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ai_review_threads" ADD CONSTRAINT "ai_review_threads_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "file_summaries" ADD CONSTRAINT "file_summaries_analysis_id_pr_review_analyses_id_fk" FOREIGN KEY ("analysis_id") REFERENCES "public"."pr_review_analyses"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_review_analyses" ADD CONSTRAINT "pr_review_analyses_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_review_analyses" ADD CONSTRAINT "pr_review_analyses_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pr_review_analyses" ADD CONSTRAINT "pr_review_analyses_triggered_by_id_users_id_fk" FOREIGN KEY ("triggered_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "sessions_user_idx" ON "sessions" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "sessions_expires_idx" ON "sessions" USING btree ("expires_at");--> statement-breakpoint +CREATE INDEX "ssh_keys_user_idx" ON "ssh_keys" USING btree ("user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "branches_repo_name_idx" ON "branches" USING btree ("repository_id","name");--> statement-breakpoint +CREATE UNIQUE INDEX "commits_repo_sha_idx" ON "commits" USING btree ("repository_id","sha");--> statement-breakpoint +CREATE INDEX "commits_repo_idx" ON "commits" USING btree ("repository_id");--> statement-breakpoint +CREATE UNIQUE INDEX "repositories_owner_name_idx" ON "repositories" USING btree ("owner_id","name");--> statement-breakpoint +CREATE UNIQUE INDEX "repositories_owner_slug_idx" ON "repositories" USING btree ("owner_id","slug");--> statement-breakpoint +CREATE INDEX "repositories_owner_idx" ON "repositories" USING btree ("owner_id");--> statement-breakpoint +CREATE UNIQUE INDEX "collaborators_repo_user_idx" ON "repository_collaborators" USING btree ("repository_id","user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "stars_repo_user_idx" ON "repository_stars" USING btree ("repository_id","user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "watchers_repo_user_idx" ON "repository_watchers" USING btree ("repository_id","user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "tags_repo_name_idx" ON "tags" USING btree ("repository_id","name");--> statement-breakpoint +CREATE INDEX "issue_comments_issue_idx" ON "issue_comments" USING btree ("issue_id");--> statement-breakpoint +CREATE UNIQUE INDEX "issues_repo_number_idx" ON "issues" USING btree ("repository_id","number");--> statement-breakpoint +CREATE INDEX "issues_repo_state_idx" ON "issues" USING btree ("repository_id","state");--> statement-breakpoint +CREATE INDEX "issues_author_idx" ON "issues" USING btree ("author_id");--> statement-breakpoint +CREATE INDEX "issues_assignee_idx" ON "issues" USING btree ("assignee_id");--> statement-breakpoint +CREATE UNIQUE INDEX "labels_repo_name_idx" ON "labels" USING btree ("repository_id","name");--> statement-breakpoint +CREATE INDEX "pr_checks_pr_idx" ON "pull_request_checks" USING btree ("pull_request_id");--> statement-breakpoint +CREATE INDEX "pr_checks_head_sha_idx" ON "pull_request_checks" USING btree ("head_sha");--> statement-breakpoint +CREATE INDEX "pr_comments_pr_idx" ON "pull_request_comments" USING btree ("pull_request_id");--> statement-breakpoint +CREATE INDEX "pr_reviews_pr_idx" ON "pull_request_reviews" USING btree ("pull_request_id");--> statement-breakpoint +CREATE UNIQUE INDEX "prs_repo_number_idx" ON "pull_requests" USING btree ("repository_id","number");--> statement-breakpoint +CREATE INDEX "prs_repo_state_idx" ON "pull_requests" USING btree ("repository_id","state");--> statement-breakpoint +CREATE INDEX "prs_author_idx" ON "pull_requests" USING btree ("author_id");--> statement-breakpoint +CREATE INDEX "workflow_jobs_run_idx" ON "workflow_jobs" USING btree ("run_id");--> statement-breakpoint +CREATE INDEX "workflow_runs_workflow_idx" ON "workflow_runs" USING btree ("workflow_id");--> statement-breakpoint +CREATE INDEX "workflow_runs_repo_idx" ON "workflow_runs" USING btree ("repository_id");--> statement-breakpoint +CREATE INDEX "workflow_runs_status_idx" ON "workflow_runs" USING btree ("status");--> statement-breakpoint +CREATE INDEX "workflows_repo_idx" ON "workflows" USING btree ("repository_id");--> statement-breakpoint +CREATE INDEX "activities_user_idx" ON "activities" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "activities_repo_idx" ON "activities" USING btree ("repository_id");--> statement-breakpoint +CREATE INDEX "activities_created_idx" ON "activities" USING btree ("created_at");--> statement-breakpoint +CREATE INDEX "audit_logs_action_idx" ON "audit_logs" USING btree ("action");--> statement-breakpoint +CREATE INDEX "audit_logs_actor_idx" ON "audit_logs" USING btree ("actor_id");--> statement-breakpoint +CREATE INDEX "audit_logs_created_idx" ON "audit_logs" USING btree ("created_at");--> statement-breakpoint +CREATE INDEX "audit_logs_target_idx" ON "audit_logs" USING btree ("target_type","target_id");--> statement-breakpoint +CREATE INDEX "notifications_user_read_idx" ON "notifications" USING btree ("user_id","is_read");--> statement-breakpoint +CREATE INDEX "notifications_user_idx" ON "notifications" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "webhook_deliveries_webhook_idx" ON "webhook_deliveries" USING btree ("webhook_id");--> statement-breakpoint +CREATE INDEX "webhook_deliveries_created_idx" ON "webhook_deliveries" USING btree ("created_at");--> statement-breakpoint +CREATE INDEX "webhooks_repo_idx" ON "webhooks" USING btree ("repository_id");--> statement-breakpoint +CREATE UNIQUE INDEX "pkg_version_idx" ON "package_versions" USING btree ("package_id","version");--> statement-breakpoint +CREATE INDEX "pkg_digest_idx" ON "package_versions" USING btree ("digest");--> statement-breakpoint +CREATE UNIQUE INDEX "pkg_org_type_name_idx" ON "packages" USING btree ("organization_id","type","name");--> statement-breakpoint +CREATE INDEX "pkg_type_idx" ON "packages" USING btree ("type");--> statement-breakpoint +CREATE INDEX "ai_inline_comments_analysis_idx" ON "ai_inline_comments" USING btree ("analysis_id");--> statement-breakpoint +CREATE INDEX "ai_inline_comments_pr_idx" ON "ai_inline_comments" USING btree ("pull_request_id");--> statement-breakpoint +CREATE INDEX "ai_inline_comments_path_idx" ON "ai_inline_comments" USING btree ("file_path");--> statement-breakpoint +CREATE INDEX "ai_inline_comments_severity_idx" ON "ai_inline_comments" USING btree ("severity");--> statement-breakpoint +CREATE INDEX "ai_thread_analysis_idx" ON "ai_review_threads" USING btree ("analysis_id");--> statement-breakpoint +CREATE INDEX "ai_thread_pr_idx" ON "ai_review_threads" USING btree ("pull_request_id");--> statement-breakpoint +CREATE INDEX "file_summaries_analysis_idx" ON "file_summaries" USING btree ("analysis_id");--> statement-breakpoint +CREATE INDEX "file_summaries_path_idx" ON "file_summaries" USING btree ("file_path");--> statement-breakpoint +CREATE INDEX "pr_analysis_pr_idx" ON "pr_review_analyses" USING btree ("pull_request_id");--> statement-breakpoint +CREATE INDEX "pr_analysis_repo_idx" ON "pr_review_analyses" USING btree ("repository_id");--> statement-breakpoint +CREATE INDEX "pr_analysis_status_idx" ON "pr_review_analyses" USING btree ("status");--> statement-breakpoint +CREATE INDEX "pr_analysis_created_idx" ON "pr_review_analyses" USING btree ("created_at"); \ No newline at end of file diff --git a/drizzle/0001_faithful_randall.sql b/drizzle/0001_faithful_randall.sql deleted file mode 100644 index 765f36ea..00000000 --- a/drizzle/0001_faithful_randall.sql +++ /dev/null @@ -1,130 +0,0 @@ -CREATE TABLE "project_cards" ( - "id" text PRIMARY KEY NOT NULL, - "column_id" text NOT NULL, - "content_id" text, - "content_type" text, - "note" text, - "order" integer NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - "creator_id" text NOT NULL -); ---> statement-breakpoint -CREATE TABLE "project_columns" ( - "id" text PRIMARY KEY NOT NULL, - "project_id" text NOT NULL, - "name" text NOT NULL, - "order" integer NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "projects" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "description" text, - "number" integer NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - "creator_id" text NOT NULL -); ---> statement-breakpoint -CREATE TABLE "merge_queue" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "pull_request_id" text NOT NULL, - "stack_id" text, - "status" text DEFAULT 'pending' NOT NULL, - "priority" integer DEFAULT 0, - "position" integer DEFAULT 0, - "ci_status" text DEFAULT 'pending', - "merge_method" text DEFAULT 'merge', - "delete_on_merge" boolean DEFAULT true, - "execution_branch" text, - "attempt_count" integer DEFAULT 0, - "last_attempt_at" timestamp, - "added_by_id" text, - "added_at" timestamp DEFAULT now() NOT NULL, - "queued_at" timestamp DEFAULT now() NOT NULL, - "started_at" timestamp, - "completed_at" timestamp, - "failure_reason" text -); ---> statement-breakpoint -CREATE TABLE "merge_queue_speculative_runs" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "pull_request_ids" text NOT NULL, - "branch_name" text NOT NULL, - "base_branch" text NOT NULL, - "status" text DEFAULT 'pending' NOT NULL, - "ci_url" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "started_at" timestamp, - "completed_at" timestamp, - "failure_reason" text, - "commit_sha" text -); ---> statement-breakpoint -CREATE TABLE "sso_configs" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "type" text DEFAULT 'oidc' NOT NULL, - "issuer" text NOT NULL, - "client_id" text NOT NULL, - "client_secret" text NOT NULL, - "scopes" text DEFAULT 'openid,profile,email', - "authorization_url" text, - "token_url" text, - "userinfo_url" text, - "jwks_uri" text, - "enabled" boolean DEFAULT false, - "auto_create_users" boolean DEFAULT true, - "allowed_domains" text, - "default_role" text DEFAULT 'member', - "organization_id" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - "created_by_id" text -); ---> statement-breakpoint -ALTER TABLE "scheduled_workflows" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_artifacts" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_jobs" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_logs" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_runs" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_secrets" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_steps" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflow_variables" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "workflows" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -ALTER TABLE "merge_queue_items" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint -DROP TABLE "scheduled_workflows" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_artifacts" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_jobs" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_logs" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_runs" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_secrets" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_steps" CASCADE;--> statement-breakpoint -DROP TABLE "workflow_variables" CASCADE;--> statement-breakpoint -DROP TABLE "workflows" CASCADE;--> statement-breakpoint -DROP TABLE "merge_queue_items" CASCADE;--> statement-breakpoint -ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_content" text;--> statement-breakpoint -ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_applied" boolean DEFAULT false;--> statement-breakpoint -ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_applied_by_id" text;--> statement-breakpoint -ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_applied_at" timestamp;--> statement-breakpoint -ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_commit_sha" text;--> statement-breakpoint -ALTER TABLE "webhooks" ADD COLUMN "provider" text DEFAULT 'generic';--> statement-breakpoint -ALTER TABLE "webhooks" ADD COLUMN "name" text;--> statement-breakpoint -ALTER TABLE "webhooks" ADD COLUMN "enabled" boolean DEFAULT true NOT NULL;--> statement-breakpoint -ALTER TABLE "project_cards" ADD CONSTRAINT "project_cards_column_id_project_columns_id_fk" FOREIGN KEY ("column_id") REFERENCES "public"."project_columns"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "project_cards" ADD CONSTRAINT "project_cards_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "project_columns" ADD CONSTRAINT "project_columns_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "projects" ADD CONSTRAINT "projects_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "projects" ADD CONSTRAINT "projects_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_added_by_id_users_id_fk" FOREIGN KEY ("added_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "merge_queue_speculative_runs" ADD CONSTRAINT "merge_queue_speculative_runs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "sso_configs" ADD CONSTRAINT "sso_configs_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_suggestion_applied_by_id_users_id_fk" FOREIGN KEY ("suggestion_applied_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0002_third_cable.sql b/drizzle/0002_third_cable.sql deleted file mode 100644 index b39e982c..00000000 --- a/drizzle/0002_third_cable.sql +++ /dev/null @@ -1,153 +0,0 @@ -CREATE TABLE "scheduled_workflows" ( - "id" text PRIMARY KEY NOT NULL, - "workflow_id" text NOT NULL, - "cron_expression" text NOT NULL, - "timezone" text DEFAULT 'UTC', - "is_enabled" boolean DEFAULT true, - "last_run_at" timestamp, - "next_run_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_artifacts" ( - "id" text PRIMARY KEY NOT NULL, - "run_id" text NOT NULL, - "job_id" text, - "name" text NOT NULL, - "size_bytes" integer NOT NULL, - "storage_path" text NOT NULL, - "mime_type" text, - "download_count" integer DEFAULT 0, - "expires_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_jobs" ( - "id" text PRIMARY KEY NOT NULL, - "run_id" text NOT NULL, - "name" text NOT NULL, - "status" text DEFAULT 'queued' NOT NULL, - "conclusion" text, - "runner_id" text, - "runner_name" text, - "runner_group_id" text, - "runner_group_name" text, - "container_id" text, - "container_image" text, - "needs" text, - "environment" text, - "environment_url" text, - "matrix" text, - "started_at" timestamp, - "completed_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_logs" ( - "id" text PRIMARY KEY NOT NULL, - "job_id" text NOT NULL, - "step_id" text, - "log_level" text DEFAULT 'info' NOT NULL, - "message" text NOT NULL, - "timestamp" text NOT NULL, - "line_number" integer, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_runs" ( - "id" text PRIMARY KEY NOT NULL, - "workflow_id" text NOT NULL, - "repository_id" text NOT NULL, - "run_number" integer NOT NULL, - "run_attempt" integer DEFAULT 1 NOT NULL, - "name" text NOT NULL, - "display_title" text, - "status" text DEFAULT 'queued' NOT NULL, - "conclusion" text, - "event" text NOT NULL, - "head_branch" text, - "head_sha" text NOT NULL, - "base_branch" text, - "base_sha" text, - "pull_request_id" text, - "triggered_by_id" text, - "started_at" timestamp, - "completed_at" timestamp, - "workflow_config" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_secrets" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "encrypted_value" text NOT NULL, - "environment" text, - "created_by_id" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_steps" ( - "id" text PRIMARY KEY NOT NULL, - "job_id" text NOT NULL, - "number" integer NOT NULL, - "name" text NOT NULL, - "status" text DEFAULT 'queued' NOT NULL, - "conclusion" text, - "uses" text, - "run" text, - "shell" text, - "working_directory" text, - "env" text, - "with" text, - "if" text, - "continue_on_error" boolean DEFAULT false, - "timeout_minutes" integer, - "started_at" timestamp, - "completed_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_variables" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "value" text NOT NULL, - "environment" text, - "created_by_id" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflows" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "path" text NOT NULL, - "state" text DEFAULT 'active' NOT NULL, - "badge_url" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -ALTER TABLE "issues" ADD COLUMN "type" text DEFAULT 'issue' NOT NULL;--> statement-breakpoint -ALTER TABLE "issues" ADD COLUMN "parent_id" text;--> statement-breakpoint -ALTER TABLE "scheduled_workflows" ADD CONSTRAINT "scheduled_workflows_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_artifacts" ADD CONSTRAINT "workflow_artifacts_run_id_workflow_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."workflow_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_artifacts" ADD CONSTRAINT "workflow_artifacts_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_jobs" ADD CONSTRAINT "workflow_jobs_run_id_workflow_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."workflow_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_logs" ADD CONSTRAINT "workflow_logs_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_logs" ADD CONSTRAINT "workflow_logs_step_id_workflow_steps_id_fk" FOREIGN KEY ("step_id") REFERENCES "public"."workflow_steps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_triggered_by_id_users_id_fk" FOREIGN KEY ("triggered_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_secrets" ADD CONSTRAINT "workflow_secrets_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_secrets" ADD CONSTRAINT "workflow_secrets_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_steps" ADD CONSTRAINT "workflow_steps_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_variables" ADD CONSTRAINT "workflow_variables_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_variables" ADD CONSTRAINT "workflow_variables_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflows" ADD CONSTRAINT "workflows_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0003_married_cyclops.sql b/drizzle/0003_married_cyclops.sql deleted file mode 100644 index bc6b4a8f..00000000 --- a/drizzle/0003_married_cyclops.sql +++ /dev/null @@ -1,590 +0,0 @@ -CREATE TABLE "team_members" ( - "team_id" text NOT NULL, - "user_id" text NOT NULL, - "role" text DEFAULT 'member' NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - CONSTRAINT "team_members_team_id_user_id_pk" PRIMARY KEY("team_id","user_id") -); ---> statement-breakpoint -CREATE TABLE "teams" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "name" text NOT NULL, - "slug" text NOT NULL, - "description" text, - "privacy" text DEFAULT 'visible', - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "repository_path_permissions" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "path_pattern" text NOT NULL, - "user_id" text, - "team_id" text, - "permission" text DEFAULT 'write' NOT NULL, - "require_approval" text DEFAULT 'false', - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "pr_state_definitions" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "display_name" text NOT NULL, - "description" text, - "color" text DEFAULT '#6B7280' NOT NULL, - "icon" text, - "order" integer DEFAULT 0 NOT NULL, - "is_default" boolean DEFAULT false, - "is_final" boolean DEFAULT false, - "allow_merge" boolean DEFAULT false, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "pr_state_reviewers" ( - "id" text PRIMARY KEY NOT NULL, - "state_definition_id" text NOT NULL, - "user_id" text, - "team_id" text, - "required_count" integer DEFAULT 1, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "pr_state_transitions" ( - "id" text PRIMARY KEY NOT NULL, - "pull_request_id" text NOT NULL, - "from_state" text, - "to_state" text NOT NULL, - "changed_by_id" text NOT NULL, - "reason" text, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "breaking_changes" ( - "id" text PRIMARY KEY NOT NULL, - "pull_request_id" text NOT NULL, - "change_type" text NOT NULL, - "severity" text NOT NULL, - "description" text NOT NULL, - "affected_files" jsonb, - "suggested_action" text, - "acknowledged" boolean DEFAULT false, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "change_set_items" ( - "id" text PRIMARY KEY NOT NULL, - "change_set_id" text NOT NULL, - "pull_request_id" text, - "repository_id" text NOT NULL, - "order" integer DEFAULT 0, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "change_sets" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "description" text, - "created_by_id" text NOT NULL, - "status" text DEFAULT 'draft' NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "chat_integrations" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text, - "repository_id" text, - "provider" text NOT NULL, - "name" text NOT NULL, - "webhook_url" text, - "api_token" text, - "channel_id" text, - "is_enabled" boolean DEFAULT true, - "events" jsonb DEFAULT '[]'::jsonb, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "cloud_configs" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text, - "organization_id" text, - "provider" text NOT NULL, - "name" text NOT NULL, - "region" text, - "credentials" jsonb, - "settings" jsonb, - "is_enabled" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "code_quality_configs" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "provider" text NOT NULL, - "project_key" text, - "api_token" text, - "server_url" text, - "webhook_secret" text, - "is_enabled" boolean DEFAULT true, - "report_on_pr" boolean DEFAULT true, - "block_on_fail" boolean DEFAULT false, - "min_coverage" real, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "code_quality_issues" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "pull_request_id" text, - "commit_sha" text NOT NULL, - "provider" text NOT NULL, - "issue_type" text NOT NULL, - "severity" text NOT NULL, - "message" text NOT NULL, - "file" text, - "line" integer, - "rule" text, - "effort" text, - "status" text DEFAULT 'open', - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "coverage_reports" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "pull_request_id" text, - "commit_sha" text NOT NULL, - "provider" text NOT NULL, - "coverage" real NOT NULL, - "lines_covered" integer, - "lines_total" integer, - "branch_coverage" real, - "delta" real, - "status" text NOT NULL, - "report_url" text, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "cross_repo_issue_links" ( - "id" text PRIMARY KEY NOT NULL, - "source_issue_id" text NOT NULL, - "target_issue_id" text NOT NULL, - "link_type" text NOT NULL, - "created_by_id" text NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "custom_dashboards" ( - "id" text PRIMARY KEY NOT NULL, - "user_id" text NOT NULL, - "name" text NOT NULL, - "description" text, - "is_public" boolean DEFAULT false, - "layout" jsonb, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "custom_field_definitions" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "field_type" text NOT NULL, - "description" text, - "is_required" boolean DEFAULT false, - "options" text, - "default_value" text, - "display_order" integer DEFAULT 0, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "custom_field_values" ( - "id" text PRIMARY KEY NOT NULL, - "issue_id" text NOT NULL, - "field_id" text NOT NULL, - "value" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "dashboard_widgets" ( - "id" text PRIMARY KEY NOT NULL, - "dashboard_id" text NOT NULL, - "widget_type" text NOT NULL, - "title" text NOT NULL, - "config" jsonb, - "position" jsonb, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "cloud_deployments" ( - "id" text PRIMARY KEY NOT NULL, - "config_id" text NOT NULL, - "pull_request_id" text, - "commit_sha" text NOT NULL, - "environment" text NOT NULL, - "status" text NOT NULL, - "url" text, - "logs" text, - "started_at" timestamp, - "completed_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "email_settings" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text, - "smtp_host" text, - "smtp_port" text, - "smtp_user" text, - "smtp_pass" text, - "from_address" text, - "from_name" text, - "is_enabled" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "external_builds" ( - "id" text PRIMARY KEY NOT NULL, - "config_id" text NOT NULL, - "pull_request_id" text, - "external_build_id" text NOT NULL, - "build_number" text, - "status" text NOT NULL, - "url" text, - "started_at" timestamp, - "completed_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "external_ci_configs" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "provider" text NOT NULL, - "name" text NOT NULL, - "base_url" text NOT NULL, - "api_token" text, - "project_id" text, - "webhook_secret" text, - "is_enabled" boolean DEFAULT true, - "sync_status" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "file_approvals" ( - "id" text PRIMARY KEY NOT NULL, - "pull_request_id" text NOT NULL, - "path" text NOT NULL, - "approved_by_id" text NOT NULL, - "approved_at" timestamp DEFAULT now() NOT NULL, - "commit_sha" text NOT NULL, - "comment" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "file_hotspots" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "file_path" text NOT NULL, - "change_count" integer DEFAULT 0, - "bug_count" integer DEFAULT 0, - "review_count" integer DEFAULT 0, - "complexity_score" real, - "last_modified" timestamp, - "calculated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "ip_allow_lists" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "name" text NOT NULL, - "cidr_block" text NOT NULL, - "description" text, - "is_enabled" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL, - "created_by_id" text NOT NULL -); ---> statement-breakpoint -CREATE TABLE "issue_tracker_configs" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "provider" text NOT NULL, - "name" text NOT NULL, - "api_url" text, - "api_token" text, - "project_key" text, - "webhook_secret" text, - "is_enabled" boolean DEFAULT true, - "sync_to_external" boolean DEFAULT true, - "sync_from_external" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "issue_tracker_links" ( - "id" text PRIMARY KEY NOT NULL, - "config_id" text NOT NULL, - "local_issue_id" text, - "external_id" text NOT NULL, - "external_key" text, - "external_url" text, - "last_sync_at" timestamp, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "license_scans" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "commit_sha" text NOT NULL, - "package_name" text NOT NULL, - "package_version" text, - "license" text NOT NULL, - "license_type" text NOT NULL, - "is_compliant" boolean NOT NULL, - "policy_violation" text, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "merge_gates" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "description" text, - "gate_type" text NOT NULL, - "config" text, - "condition_script" text, - "is_enabled" boolean DEFAULT true, - "order" integer DEFAULT 0, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "metric_snapshots" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text, - "organization_id" text, - "metric_type" text NOT NULL, - "value" real NOT NULL, - "dimensions" jsonb, - "timestamp" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "migration_detections" ( - "id" text PRIMARY KEY NOT NULL, - "pull_request_id" text NOT NULL, - "migration_type" text NOT NULL, - "tool" text, - "files" jsonb, - "is_reversible" boolean, - "requires_downtime" boolean, - "notes" text, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "pr_issue_links" ( - "id" text PRIMARY KEY NOT NULL, - "pull_request_id" text NOT NULL, - "issue_id" text NOT NULL, - "link_type" text NOT NULL, - "created_by_id" text NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "rate_limit_logs" ( - "id" text PRIMARY KEY NOT NULL, - "rule_id" text NOT NULL, - "key" text NOT NULL, - "request_count" integer NOT NULL, - "window_start" timestamp NOT NULL, - "blocked" boolean DEFAULT false -); ---> statement-breakpoint -CREATE TABLE "rate_limit_rules" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "path" text NOT NULL, - "method" text, - "window_ms" integer NOT NULL, - "max_requests" integer NOT NULL, - "key_type" text DEFAULT 'ip', - "is_enabled" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "repository_workflows" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "path" text NOT NULL, - "content" text NOT NULL, - "template_id" text, - "is_enabled" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "required_status_checks" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "branch" text NOT NULL, - "check_name" text NOT NULL, - "is_required" boolean DEFAULT true, - "strict_mode" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "review_templates" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "organization_id" text, - "name" text NOT NULL, - "description" text, - "category" text, - "content" text NOT NULL, - "is_default" boolean DEFAULT false NOT NULL, - "created_by_id" text NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "saml_configs" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "entity_id" text NOT NULL, - "sso_url" text NOT NULL, - "certificate" text NOT NULL, - "signature_algorithm" text DEFAULT 'RSA-SHA256', - "digest_algorithm" text DEFAULT 'SHA256', - "is_enabled" boolean DEFAULT true, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "secret_scan_results" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "commit_sha" text NOT NULL, - "secret_type" text NOT NULL, - "file" text NOT NULL, - "line" integer, - "snippet" text, - "severity" text NOT NULL, - "status" text DEFAULT 'open', - "resolved_at" timestamp, - "resolved_by_id" text, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_states" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "name" text NOT NULL, - "category" text NOT NULL, - "color" text DEFAULT '#6b7280', - "icon" text, - "display_order" integer DEFAULT 0, - "is_default" boolean DEFAULT false, - "is_closed_state" boolean DEFAULT false, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_templates" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "description" text, - "category" text NOT NULL, - "language" text, - "content" text NOT NULL, - "is_official" boolean DEFAULT false, - "is_public" boolean DEFAULT true, - "created_by_id" text, - "downloads" text DEFAULT '0', - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "workflow_transitions" ( - "id" text PRIMARY KEY NOT NULL, - "repository_id" text NOT NULL, - "from_state_id" text, - "to_state_id" text NOT NULL, - "requires_comment" boolean DEFAULT false, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -ALTER TABLE "deploy_keys" ALTER COLUMN "read_only" SET NOT NULL;--> statement-breakpoint -ALTER TABLE "deploy_keys" ADD COLUMN "fingerprint" text NOT NULL;--> statement-breakpoint -ALTER TABLE "deploy_keys" ADD COLUMN "last_used_at" timestamp;--> statement-breakpoint -ALTER TABLE "repositories" ADD COLUMN "is_template" boolean DEFAULT false;--> statement-breakpoint -ALTER TABLE "repositories" ADD COLUMN "last_mirror_sync_at" timestamp;--> statement-breakpoint -ALTER TABLE "repositories" ADD COLUMN "mirror_sync_status" text;--> statement-breakpoint -ALTER TABLE "pull_request_checks" ADD COLUMN "completed_at" timestamp;--> statement-breakpoint -ALTER TABLE "pull_request_checks" ADD COLUMN "created_at" timestamp DEFAULT now() NOT NULL;--> statement-breakpoint -ALTER TABLE "pull_requests" ADD COLUMN "auto_merge_enabled_by_id" text;--> statement-breakpoint -ALTER TABLE "pull_requests" ADD COLUMN "auto_merge_enabled_at" timestamp;--> statement-breakpoint -ALTER TABLE "pull_requests" ADD COLUMN "custom_state" text;--> statement-breakpoint -ALTER TABLE "pull_requests" ADD COLUMN "custom_state_changed_at" timestamp;--> statement-breakpoint -ALTER TABLE "audit_logs" ADD COLUMN "organization_id" text;--> statement-breakpoint -ALTER TABLE "team_members" ADD CONSTRAINT "team_members_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "team_members" ADD CONSTRAINT "team_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "teams" ADD CONSTRAINT "teams_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_state_definitions" ADD CONSTRAINT "pr_state_definitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_state_definition_id_pr_state_definitions_id_fk" FOREIGN KEY ("state_definition_id") REFERENCES "public"."pr_state_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_state_transitions" ADD CONSTRAINT "pr_state_transitions_changed_by_id_users_id_fk" FOREIGN KEY ("changed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "breaking_changes" ADD CONSTRAINT "breaking_changes_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_change_set_id_change_sets_id_fk" FOREIGN KEY ("change_set_id") REFERENCES "public"."change_sets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "change_sets" ADD CONSTRAINT "change_sets_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "cloud_configs" ADD CONSTRAINT "cloud_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "code_quality_configs" ADD CONSTRAINT "code_quality_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "code_quality_issues" ADD CONSTRAINT "code_quality_issues_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "coverage_reports" ADD CONSTRAINT "coverage_reports_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "cross_repo_issue_links" ADD CONSTRAINT "cross_repo_issue_links_source_issue_id_issues_id_fk" FOREIGN KEY ("source_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "cross_repo_issue_links" ADD CONSTRAINT "cross_repo_issue_links_target_issue_id_issues_id_fk" FOREIGN KEY ("target_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "custom_dashboards" ADD CONSTRAINT "custom_dashboards_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "custom_field_definitions" ADD CONSTRAINT "custom_field_definitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "custom_field_values" ADD CONSTRAINT "custom_field_values_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "custom_field_values" ADD CONSTRAINT "custom_field_values_field_id_custom_field_definitions_id_fk" FOREIGN KEY ("field_id") REFERENCES "public"."custom_field_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "dashboard_widgets" ADD CONSTRAINT "dashboard_widgets_dashboard_id_custom_dashboards_id_fk" FOREIGN KEY ("dashboard_id") REFERENCES "public"."custom_dashboards"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "cloud_deployments" ADD CONSTRAINT "cloud_deployments_config_id_cloud_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."cloud_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "external_builds" ADD CONSTRAINT "external_builds_config_id_external_ci_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."external_ci_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "external_ci_configs" ADD CONSTRAINT "external_ci_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "file_approvals" ADD CONSTRAINT "file_approvals_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "file_approvals" ADD CONSTRAINT "file_approvals_approved_by_id_users_id_fk" FOREIGN KEY ("approved_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "file_hotspots" ADD CONSTRAINT "file_hotspots_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "issue_tracker_configs" ADD CONSTRAINT "issue_tracker_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "issue_tracker_links" ADD CONSTRAINT "issue_tracker_links_config_id_issue_tracker_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."issue_tracker_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "issue_tracker_links" ADD CONSTRAINT "issue_tracker_links_local_issue_id_issues_id_fk" FOREIGN KEY ("local_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "license_scans" ADD CONSTRAINT "license_scans_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "merge_gates" ADD CONSTRAINT "merge_gates_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "metric_snapshots" ADD CONSTRAINT "metric_snapshots_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "migration_detections" ADD CONSTRAINT "migration_detections_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_issue_links" ADD CONSTRAINT "pr_issue_links_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pr_issue_links" ADD CONSTRAINT "pr_issue_links_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "rate_limit_logs" ADD CONSTRAINT "rate_limit_logs_rule_id_rate_limit_rules_id_fk" FOREIGN KEY ("rule_id") REFERENCES "public"."rate_limit_rules"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "repository_workflows" ADD CONSTRAINT "repository_workflows_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "repository_workflows" ADD CONSTRAINT "repository_workflows_template_id_workflow_templates_id_fk" FOREIGN KEY ("template_id") REFERENCES "public"."workflow_templates"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "required_status_checks" ADD CONSTRAINT "required_status_checks_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "review_templates" ADD CONSTRAINT "review_templates_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "secret_scan_results" ADD CONSTRAINT "secret_scan_results_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_states" ADD CONSTRAINT "workflow_states_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_from_state_id_workflow_states_id_fk" FOREIGN KEY ("from_state_id") REFERENCES "public"."workflow_states"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_to_state_id_workflow_states_id_fk" FOREIGN KEY ("to_state_id") REFERENCES "public"."workflow_states"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_auto_merge_enabled_by_id_users_id_fk" FOREIGN KEY ("auto_merge_enabled_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0004_add_performance_indexes.sql b/drizzle/0004_add_performance_indexes.sql deleted file mode 100644 index bf40adbb..00000000 --- a/drizzle/0004_add_performance_indexes.sql +++ /dev/null @@ -1,267 +0,0 @@ --- Migration 0004: Add missing performance indexes --- --- Adds indexes for foreign keys, status columns, and query patterns that were --- previously unindexed. These are critical for production scale: --- * O(N) table scans become O(log N) index lookups --- * Merge queue, AI reviews, and CI tables are the hottest query targets --- * Composite indexes target the specific (a, b) WHERE/ORDER patterns used --- in dashboards, list pages, and worker pickups --- --- All indexes are additive (CREATE INDEX) and use IF NOT EXISTS so this --- migration is safe to re-run. --- --- Generated to match the schema changes in src/db/schema/ for tables that --- previously had no `index(...)` or `uniqueIndex(...)` extra-config callback. - --- ============================================================================ --- MERGE QUEUE --- ============================================================================ -CREATE INDEX IF NOT EXISTS "merge_queue_repo_status_idx" ON "merge_queue" ("repository_id", "status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "merge_queue_repo_position_idx" ON "merge_queue" ("repository_id", "position");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "merge_queue_pr_status_idx" ON "merge_queue" ("pull_request_id", "status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "merge_queue_status_idx" ON "merge_queue" ("status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "merge_queue_stack_idx" ON "merge_queue" ("stack_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "merge_queue_speculative_runs_repo_idx" ON "merge_queue_speculative_runs" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "merge_queue_speculative_runs_repo_status_idx" ON "merge_queue_speculative_runs" ("repository_id", "status");--> statement-breakpoint - --- ============================================================================ --- STACKED PRS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "pr_stacks_repo_idx" ON "pr_stacks" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_stacks_repo_status_idx" ON "pr_stacks" ("repository_id", "status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_stack_entries_stack_order_idx" ON "pr_stack_entries" ("stack_id", "stack_order");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_stack_entries_pr_idx" ON "pr_stack_entries" ("pull_request_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "pr_stack_entries_stack_order_uniq" ON "pr_stack_entries" ("stack_id", "stack_order");--> statement-breakpoint - --- ============================================================================ --- WORKFLOWS (CI/CD) --- ============================================================================ -CREATE INDEX IF NOT EXISTS "workflow_jobs_status_idx" ON "workflow_jobs" ("status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_jobs_runner_idx" ON "workflow_jobs" ("runner_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_steps_job_idx" ON "workflow_steps" ("job_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_steps_job_number_idx" ON "workflow_steps" ("job_id", "number");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_logs_job_idx" ON "workflow_logs" ("job_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_logs_step_idx" ON "workflow_logs" ("step_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_logs_job_created_idx" ON "workflow_logs" ("job_id", "created_at");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_artifacts_run_idx" ON "workflow_artifacts" ("run_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_artifacts_job_idx" ON "workflow_artifacts" ("job_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_artifacts_expires_idx" ON "workflow_artifacts" ("expires_at");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_secrets_repo_idx" ON "workflow_secrets" ("repository_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "workflow_secrets_repo_name_env_uniq" ON "workflow_secrets" ("repository_id", "name", "environment");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "workflow_variables_repo_idx" ON "workflow_variables" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "scheduled_workflows_workflow_idx" ON "scheduled_workflows" ("workflow_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "scheduled_workflows_enabled_next_idx" ON "scheduled_workflows" ("is_enabled", "next_run_at");--> statement-breakpoint - --- ============================================================================ --- AI REVIEWS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "ai_reviews_pr_created_idx" ON "ai_reviews" ("pull_request_id", "created_at");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "ai_reviews_status_idx" ON "ai_reviews" ("status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "ai_review_suggestions_review_idx" ON "ai_review_suggestions" ("ai_review_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "ai_review_suggestions_review_severity_idx" ON "ai_review_suggestions" ("ai_review_id", "severity");--> statement-breakpoint - --- ============================================================================ --- ISSUES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "issue_labels_issue_idx" ON "issue_labels" ("issue_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_labels_label_idx" ON "issue_labels" ("label_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "milestones_repo_idx" ON "milestones" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "milestones_repo_state_idx" ON "milestones" ("repository_id", "state");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_assignees_issue_idx" ON "issue_assignees" ("issue_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_assignees_user_idx" ON "issue_assignees" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_subscribers_issue_idx" ON "issue_subscribers" ("issue_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_subscribers_user_idx" ON "issue_subscribers" ("user_id");--> statement-breakpoint - --- ============================================================================ --- PULL REQUESTS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "pr_labels_pr_idx" ON "pull_request_labels" ("pull_request_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_labels_label_idx" ON "pull_request_labels" ("label_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_assignees_pr_idx" ON "pull_request_assignees" ("pull_request_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_assignees_user_idx" ON "pull_request_assignees" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_reviewers_pr_idx" ON "pull_request_reviewers" ("pull_request_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_reviewers_user_idx" ON "pull_request_reviewers" ("user_id");--> statement-breakpoint - --- ============================================================================ --- ORGS / TEAMS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "organizations_display_name_idx" ON "organizations" ("display_name");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "organization_members_user_idx" ON "organization_members" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "organization_members_org_role_idx" ON "organization_members" ("organization_id", "role");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "teams_org_idx" ON "teams" ("organization_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "teams_org_slug_uniq" ON "teams" ("organization_id", "slug");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "team_members_user_idx" ON "team_members" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "team_repositories_repo_idx" ON "team_repositories" ("repository_id");--> statement-breakpoint - --- ============================================================================ --- PROJECTS --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "projects_repo_number_uniq" ON "projects" ("repository_id", "number");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "projects_repo_idx" ON "projects" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "project_columns_project_idx" ON "project_columns" ("project_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "project_columns_project_order_idx" ON "project_columns" ("project_id", "order");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "project_cards_column_idx" ON "project_cards" ("column_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "project_cards_column_order_idx" ON "project_cards" ("column_id", "order");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "project_cards_content_idx" ON "project_cards" ("content_type", "content_id");--> statement-breakpoint - --- ============================================================================ --- WIKI --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "wiki_pages_repo_slug_uniq" ON "wiki_pages" ("repository_id", "slug");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "wiki_pages_repo_order_idx" ON "wiki_pages" ("repository_id", "order");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "wiki_pages_parent_idx" ON "wiki_pages" ("parent_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "wiki_revisions_page_created_idx" ON "wiki_revisions" ("page_id", "created_at");--> statement-breakpoint - --- ============================================================================ --- SECURITY --- ============================================================================ -CREATE INDEX IF NOT EXISTS "security_scans_repo_idx" ON "security_scans" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "security_scans_repo_status_idx" ON "security_scans" ("repository_id", "status");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "security_vulns_scan_severity_idx" ON "security_vulnerabilities" ("scan_id", "severity");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "security_vulns_vuln_id_idx" ON "security_vulnerabilities" ("vulnerability_id");--> statement-breakpoint - --- ============================================================================ --- WEBHOOKS --- Add encrypted-at-rest secret + display hint columns. The legacy `secret` --- column previously stored a SHA-256 hash, which is unusable as an HMAC key; --- see src/lib/webhooks.ts resolveSigningSecret() for the compat shim. --- ============================================================================ -ALTER TABLE "webhooks" ADD COLUMN IF NOT EXISTS "secret_ciphertext" text;--> statement-breakpoint -ALTER TABLE "webhooks" ADD COLUMN IF NOT EXISTS "secret_hint" text;--> statement-breakpoint - --- ============================================================================ --- AUTOMATIONS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "automation_rules_trigger_enabled_idx" ON "automation_rules" ("trigger", "is_enabled");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "automation_rules_repo_idx" ON "automation_rules" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "automation_rules_org_idx" ON "automation_rules" ("organization_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "automation_executions_rule_created_idx" ON "automation_executions" ("rule_id", "created_at");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "automation_executions_status_idx" ON "automation_executions" ("status");--> statement-breakpoint - --- ============================================================================ --- NOTIFICATIONS --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "notification_prefs_user_event_uniq" ON "notification_preferences" ("user_id", "event_type", "repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "notification_prefs_repo_idx" ON "notification_preferences" ("repository_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "notification_quiet_hours_user_uniq" ON "notification_quiet_hours" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "push_subscriptions_user_active_idx" ON "push_subscriptions" ("user_id", "is_active");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "push_subscriptions_endpoint_uniq" ON "push_subscriptions" ("endpoint");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "email_digest_settings_user_uniq" ON "email_digest_settings" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "email_digest_settings_digest_type_idx" ON "email_digest_settings" ("digest_type");--> statement-breakpoint - --- ============================================================================ --- PR STATES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "pr_state_defs_repo_order_idx" ON "pr_state_definitions" ("repository_id", "order");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_state_reviewers_state_idx" ON "pr_state_reviewers" ("state_definition_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_state_reviewers_user_idx" ON "pr_state_reviewers" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_state_reviewers_team_idx" ON "pr_state_reviewers" ("team_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_state_transitions_pr_created_idx" ON "pr_state_transitions" ("pull_request_id", "created_at");--> statement-breakpoint - --- ============================================================================ --- BRANCH PROTECTION --- ============================================================================ -CREATE INDEX IF NOT EXISTS "branch_protection_repo_pattern_idx" ON "branch_protection" ("repository_id", "pattern");--> statement-breakpoint - --- ============================================================================ --- DEPLOY KEYS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "deploy_keys_repo_idx" ON "deploy_keys" ("repository_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "deploy_keys_fingerprint_uniq" ON "deploy_keys" ("fingerprint");--> statement-breakpoint - --- ============================================================================ --- PIPELINE RUNNERS --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "pipeline_runners_token_uniq" ON "pipeline_runners" ("token");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pipeline_runners_status_last_seen_idx" ON "pipeline_runners" ("status", "last_seen_at");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pipeline_runners_repo_idx" ON "pipeline_runners" ("repository_id");--> statement-breakpoint - --- ============================================================================ --- EXTERNAL CI --- ============================================================================ -CREATE INDEX IF NOT EXISTS "external_ci_repo_idx" ON "external_ci_integrations" ("repository_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "external_ci_token_hash_uniq" ON "external_ci_integrations" ("token_hash");--> statement-breakpoint - --- ============================================================================ --- AUTO MERGE RULES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "auto_merge_rules_repo_enabled_idx" ON "auto_merge_rules" ("repository_id", "is_enabled");--> statement-breakpoint - --- ============================================================================ --- REVIEW RULES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "review_requirements_repo_idx" ON "review_requirements" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "reviewer_rules_repo_enabled_idx" ON "reviewer_rules" ("repository_id", "is_enabled");--> statement-breakpoint - --- ============================================================================ --- SECURITY POLICIES --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "security_policies_repo_uniq" ON "security_policies" ("repository_id");--> statement-breakpoint - --- ============================================================================ --- SSO --- ============================================================================ -CREATE INDEX IF NOT EXISTS "sso_configs_org_idx" ON "sso_configs" ("organization_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "sso_configs_issuer_uniq" ON "sso_configs" ("issuer");--> statement-breakpoint - --- ============================================================================ --- PATH PERMISSIONS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "repo_path_perms_repo_pattern_idx" ON "repository_path_permissions" ("repository_id", "path_pattern");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "repo_path_perms_user_idx" ON "repository_path_permissions" ("user_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "repo_path_perms_team_idx" ON "repository_path_permissions" ("team_id");--> statement-breakpoint - --- ============================================================================ --- INBOX SECTIONS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "inbox_sections_user_position_idx" ON "inbox_sections" ("user_id", "position");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "shared_inbox_sections_section_idx" ON "shared_inbox_sections" ("section_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "shared_inbox_sections_user_idx" ON "shared_inbox_sections" ("shared_with_user_id");--> statement-breakpoint - --- ============================================================================ --- CUSTOM FIELDS --- ============================================================================ -CREATE INDEX IF NOT EXISTS "custom_field_defs_repo_order_idx" ON "custom_field_definitions" ("repository_id", "order");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_custom_field_values_issue_idx" ON "issue_custom_field_values" ("issue_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "issue_custom_field_values_field_idx" ON "issue_custom_field_values" ("field_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "issue_custom_field_values_issue_field_uniq" ON "issue_custom_field_values" ("issue_id", "field_id");--> statement-breakpoint - --- ============================================================================ --- ISSUE STATUSES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "issue_statuses_repo_order_idx" ON "issue_statuses" ("repository_id", "order");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "issue_statuses_repo_name_uniq" ON "issue_statuses" ("repository_id", "name");--> statement-breakpoint - --- ============================================================================ --- ROLES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "custom_roles_org_idx" ON "custom_roles" ("organization_id");--> statement-breakpoint - --- ============================================================================ --- SYSTEM CONFIG --- ============================================================================ -CREATE INDEX IF NOT EXISTS "system_config_updated_by_idx" ON "system_config" ("updated_by_id");--> statement-breakpoint - --- ============================================================================ --- DEVELOPER METRICS --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "pr_metrics_pr_uniq" ON "pr_metrics" ("pull_request_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_metrics_author_created_idx" ON "pr_metrics" ("author_id", "pr_created_at");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "pr_metrics_repo_created_idx" ON "pr_metrics" ("repository_id", "pr_created_at");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "review_metrics_user_week_uniq" ON "review_metrics" ("user_id", "week_of");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "repo_metrics_repo_week_uniq" ON "repo_metrics" ("repository_id", "week_of");--> statement-breakpoint - --- ============================================================================ --- AI REVIEW RULES --- ============================================================================ -CREATE INDEX IF NOT EXISTS "ai_review_rules_repo_enabled_idx" ON "ai_review_rules" ("repository_id", "is_enabled");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "ai_review_rules_org_idx" ON "ai_review_rules" ("organization_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "ai_codebase_context_repo_idx" ON "ai_codebase_context" ("repository_id");--> statement-breakpoint - --- ============================================================================ --- SLACK INTEGRATION --- ============================================================================ -CREATE UNIQUE INDEX IF NOT EXISTS "slack_workspaces_team_id_uniq" ON "slack_workspaces" ("team_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "slack_channel_mappings_repo_idx" ON "slack_channel_mappings" ("repository_id");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "slack_channel_mappings_workspace_idx" ON "slack_channel_mappings" ("workspace_id");--> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "slack_user_mappings_user_workspace_uniq" ON "slack_user_mappings" ("user_id", "workspace_id");--> statement-breakpoint diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 00000000..d3c1fda9 --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,15641 @@ +{ + "id": "ef69ec29-f876-4b7f-bb29-bf818be46981", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.email_verification_tokens": { + "name": "email_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "email_verification_tokens_user_id_users_id_fk": { + "name": "email_verification_tokens_user_id_users_id_fk", + "tableFrom": "email_verification_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "email_verification_tokens_token_unique": { + "name": "email_verification_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gpg_keys": { + "name": "gpg_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_id": { + "name": "key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "gpg_keys_user_id_users_id_fk": { + "name": "gpg_keys_user_id_users_id_fk", + "tableFrom": "gpg_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_accounts": { + "name": "oauth_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_accounts_user_id_users_id_fk": { + "name": "oauth_accounts_user_id_users_id_fk", + "tableFrom": "oauth_accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "password_reset_tokens_token_unique": { + "name": "password_reset_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.personal_access_tokens": { + "name": "personal_access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "personal_access_tokens_user_id_users_id_fk": { + "name": "personal_access_tokens_user_id_users_id_fk", + "tableFrom": "personal_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "personal_access_tokens_token_unique": { + "name": "personal_access_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sessions_user_idx": { + "name": "sessions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_expires_idx": { + "name": "sessions_expires_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh_keys": { + "name": "ssh_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_type": { + "name": "key_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ssh_keys_user_idx": { + "name": "ssh_keys_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ssh_keys_user_id_users_id_fk": { + "name": "ssh_keys_user_id_users_id_fk", + "tableFrom": "ssh_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "ssh_keys_fingerprint_unique": { + "name": "ssh_keys_fingerprint_unique", + "nullsNotDistinct": false, + "columns": [ + "fingerprint" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_followers": { + "name": "user_followers", + "schema": "", + "columns": { + "follower_id": { + "name": "follower_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "followee_id": { + "name": "followee_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_followers_follower_id_users_id_fk": { + "name": "user_followers_follower_id_users_id_fk", + "tableFrom": "user_followers", + "tableTo": "users", + "columnsFrom": [ + "follower_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_followers_followee_id_users_id_fk": { + "name": "user_followers_followee_id_users_id_fk", + "tableFrom": "user_followers", + "tableTo": "users", + "columnsFrom": [ + "followee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_followers_follower_id_followee_id_pk": { + "name": "user_followers_follower_id_followee_id_pk", + "columns": [ + "follower_id", + "followee_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "company": { + "name": "company", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "two_factor_secret": { + "name": "two_factor_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_login_at": { + "name": "last_login_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ai_config": { + "name": "ai_config", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_username_unique": { + "name": "users_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + }, + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deploy_keys": { + "name": "deploy_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "read_only": { + "name": "read_only", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deploy_keys_repository_id_repositories_id_fk": { + "name": "deploy_keys_repository_id_repositories_id_fk", + "tableFrom": "deploy_keys", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.branches": { + "name": "branches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_protected": { + "name": "is_protected", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "protection_rules": { + "name": "protection_rules", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "branches_repo_name_idx": { + "name": "branches_repo_name_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "branches_repository_id_repositories_id_fk": { + "name": "branches_repository_id_repositories_id_fk", + "tableFrom": "branches", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commits": { + "name": "commits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sha": { + "name": "sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_email": { + "name": "author_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_date": { + "name": "author_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "committer_name": { + "name": "committer_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "committer_email": { + "name": "committer_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "committer_date": { + "name": "committer_date", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "parent_shas": { + "name": "parent_shas", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tree_sha": { + "name": "tree_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stats": { + "name": "stats", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_verified": { + "name": "is_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "commits_repo_sha_idx": { + "name": "commits_repo_sha_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "commits_repo_idx": { + "name": "commits_repo_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commits_repository_id_repositories_id_fk": { + "name": "commits_repository_id_repositories_id_fk", + "tableFrom": "commits", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commits_user_id_users_id_fk": { + "name": "commits_user_id_users_id_fk", + "tableFrom": "commits", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.releases": { + "name": "releases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tag_id": { + "name": "tag_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_draft": { + "name": "is_draft", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_prerelease": { + "name": "is_prerelease", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assets": { + "name": "assets", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "published_at": { + "name": "published_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "releases_repository_id_repositories_id_fk": { + "name": "releases_repository_id_repositories_id_fk", + "tableFrom": "releases", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "releases_tag_id_tags_id_fk": { + "name": "releases_tag_id_tags_id_fk", + "tableFrom": "releases", + "tableTo": "tags", + "columnsFrom": [ + "tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "releases_author_id_users_id_fk": { + "name": "releases_author_id_users_id_fk", + "tableFrom": "releases", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repositories": { + "name": "repositories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_type": { + "name": "owner_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'main'" + }, + "disk_path": { + "name": "disk_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ssh_clone_url": { + "name": "ssh_clone_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_clone_url": { + "name": "http_clone_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "star_count": { + "name": "star_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fork_count": { + "name": "fork_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "watch_count": { + "name": "watch_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "open_issue_count": { + "name": "open_issue_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "open_pr_count": { + "name": "open_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_fork": { + "name": "is_fork", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "forked_from_id": { + "name": "forked_from_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_archived": { + "name": "is_archived", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_template": { + "name": "is_template", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_mirror": { + "name": "is_mirror", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "mirror_url": { + "name": "mirror_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_mirror_sync_at": { + "name": "last_mirror_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "mirror_sync_status": { + "name": "mirror_sync_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_issues": { + "name": "has_issues", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "has_wiki": { + "name": "has_wiki", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "has_actions": { + "name": "has_actions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "allow_forking": { + "name": "allow_forking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "license_type": { + "name": "license_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "topics": { + "name": "topics", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "languages": { + "name": "languages", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_activity_at": { + "name": "last_activity_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "repositories_owner_name_idx": { + "name": "repositories_owner_name_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "repositories_owner_slug_idx": { + "name": "repositories_owner_slug_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "repositories_owner_idx": { + "name": "repositories_owner_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "repositories_owner_id_users_id_fk": { + "name": "repositories_owner_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repositories_forked_from_id_repositories_id_fk": { + "name": "repositories_forked_from_id_repositories_id_fk", + "tableFrom": "repositories", + "tableTo": "repositories", + "columnsFrom": [ + "forked_from_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repository_collaborators": { + "name": "repository_collaborators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'developer'" + }, + "added_by_id": { + "name": "added_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "collaborators_repo_user_idx": { + "name": "collaborators_repo_user_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "repository_collaborators_repository_id_repositories_id_fk": { + "name": "repository_collaborators_repository_id_repositories_id_fk", + "tableFrom": "repository_collaborators", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_collaborators_user_id_users_id_fk": { + "name": "repository_collaborators_user_id_users_id_fk", + "tableFrom": "repository_collaborators", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_collaborators_added_by_id_users_id_fk": { + "name": "repository_collaborators_added_by_id_users_id_fk", + "tableFrom": "repository_collaborators", + "tableTo": "users", + "columnsFrom": [ + "added_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repository_stars": { + "name": "repository_stars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "stars_repo_user_idx": { + "name": "stars_repo_user_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "repository_stars_repository_id_repositories_id_fk": { + "name": "repository_stars_repository_id_repositories_id_fk", + "tableFrom": "repository_stars", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_stars_user_id_users_id_fk": { + "name": "repository_stars_user_id_users_id_fk", + "tableFrom": "repository_stars", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repository_watchers": { + "name": "repository_watchers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "watch_level": { + "name": "watch_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'watching'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "watchers_repo_user_idx": { + "name": "watchers_repo_user_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "repository_watchers_repository_id_repositories_id_fk": { + "name": "repository_watchers_repository_id_repositories_id_fk", + "tableFrom": "repository_watchers", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_watchers_user_id_users_id_fk": { + "name": "repository_watchers_user_id_users_id_fk", + "tableFrom": "repository_watchers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tags": { + "name": "tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tagger_name": { + "name": "tagger_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tagger_email": { + "name": "tagger_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tagged_at": { + "name": "tagged_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_release": { + "name": "is_release", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "release_id": { + "name": "release_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tags_repo_name_idx": { + "name": "tags_repo_name_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tags_repository_id_repositories_id_fk": { + "name": "tags_repository_id_repositories_id_fk", + "tableFrom": "tags", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_assignees": { + "name": "issue_assignees", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "issue_assignees_issue_id_issues_id_fk": { + "name": "issue_assignees_issue_id_issues_id_fk", + "tableFrom": "issue_assignees", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_assignees_user_id_users_id_fk": { + "name": "issue_assignees_user_id_users_id_fk", + "tableFrom": "issue_assignees", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_comments": { + "name": "issue_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reactions": { + "name": "reactions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_edited": { + "name": "is_edited", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "edited_at": { + "name": "edited_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issue_comments_issue_idx": { + "name": "issue_comments_issue_idx", + "columns": [ + { + "expression": "issue_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issue_comments_issue_id_issues_id_fk": { + "name": "issue_comments_issue_id_issues_id_fk", + "tableFrom": "issue_comments", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_comments_author_id_users_id_fk": { + "name": "issue_comments_author_id_users_id_fk", + "tableFrom": "issue_comments", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_labels": { + "name": "issue_labels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label_id": { + "name": "label_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "issue_labels_issue_id_issues_id_fk": { + "name": "issue_labels_issue_id_issues_id_fk", + "tableFrom": "issue_labels", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_labels_label_id_labels_id_fk": { + "name": "issue_labels_label_id_labels_id_fk", + "tableFrom": "issue_labels", + "tableTo": "labels", + "columnsFrom": [ + "label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_subscribers": { + "name": "issue_subscribers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscribed_at": { + "name": "subscribed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "issue_subscribers_issue_id_issues_id_fk": { + "name": "issue_subscribers_issue_id_issues_id_fk", + "tableFrom": "issue_subscribers", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_subscribers_user_id_users_id_fk": { + "name": "issue_subscribers_user_id_users_id_fk", + "tableFrom": "issue_subscribers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issues": { + "name": "issues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "status_id": { + "name": "status_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'issue'" + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee_id": { + "name": "assignee_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "milestone_id": { + "name": "milestone_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_pinned": { + "name": "is_pinned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "lock_reason": { + "name": "lock_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "comment_count": { + "name": "comment_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "reactions": { + "name": "reactions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "closed_by_id": { + "name": "closed_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "issues_repo_number_idx": { + "name": "issues_repo_number_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_repo_state_idx": { + "name": "issues_repo_state_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_author_idx": { + "name": "issues_author_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "issues_assignee_idx": { + "name": "issues_assignee_idx", + "columns": [ + { + "expression": "assignee_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "issues_repository_id_repositories_id_fk": { + "name": "issues_repository_id_repositories_id_fk", + "tableFrom": "issues", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issues_status_id_issue_statuses_id_fk": { + "name": "issues_status_id_issue_statuses_id_fk", + "tableFrom": "issues", + "tableTo": "issue_statuses", + "columnsFrom": [ + "status_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_author_id_users_id_fk": { + "name": "issues_author_id_users_id_fk", + "tableFrom": "issues", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_assignee_id_users_id_fk": { + "name": "issues_assignee_id_users_id_fk", + "tableFrom": "issues", + "tableTo": "users", + "columnsFrom": [ + "assignee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "issues_closed_by_id_users_id_fk": { + "name": "issues_closed_by_id_users_id_fk", + "tableFrom": "issues", + "tableTo": "users", + "columnsFrom": [ + "closed_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.labels": { + "name": "labels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#6b7280'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "labels_repo_name_idx": { + "name": "labels_repo_name_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "labels_repository_id_repositories_id_fk": { + "name": "labels_repository_id_repositories_id_fk", + "tableFrom": "labels", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.milestones": { + "name": "milestones", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "due_date": { + "name": "due_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "open_issue_count": { + "name": "open_issue_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "closed_issue_count": { + "name": "closed_issue_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "milestones_repository_id_repositories_id_fk": { + "name": "milestones_repository_id_repositories_id_fk", + "tableFrom": "milestones", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_request_assignees": { + "name": "pull_request_assignees", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pull_request_assignees_pull_request_id_pull_requests_id_fk": { + "name": "pull_request_assignees_pull_request_id_pull_requests_id_fk", + "tableFrom": "pull_request_assignees", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pull_request_assignees_user_id_users_id_fk": { + "name": "pull_request_assignees_user_id_users_id_fk", + "tableFrom": "pull_request_assignees", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_request_checks": { + "name": "pull_request_checks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conclusion": { + "name": "conclusion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "details_url": { + "name": "details_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output": { + "name": "output", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pr_checks_pr_idx": { + "name": "pr_checks_pr_idx", + "columns": [ + { + "expression": "pull_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pr_checks_head_sha_idx": { + "name": "pr_checks_head_sha_idx", + "columns": [ + { + "expression": "head_sha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pull_request_checks_pull_request_id_pull_requests_id_fk": { + "name": "pull_request_checks_pull_request_id_pull_requests_id_fk", + "tableFrom": "pull_request_checks", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_request_comments": { + "name": "pull_request_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "review_id": { + "name": "review_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line": { + "name": "line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "side": { + "name": "side", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_line": { + "name": "start_line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "start_side": { + "name": "start_side", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_commit_sha": { + "name": "original_commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_line": { + "name": "original_line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "in_reply_to_id": { + "name": "in_reply_to_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reactions": { + "name": "reactions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_resolved": { + "name": "is_resolved", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "resolved_by_id": { + "name": "resolved_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_edited": { + "name": "is_edited", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "edited_at": { + "name": "edited_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "suggestion_content": { + "name": "suggestion_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "suggestion_applied": { + "name": "suggestion_applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "suggestion_applied_by_id": { + "name": "suggestion_applied_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "suggestion_applied_at": { + "name": "suggestion_applied_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "suggestion_commit_sha": { + "name": "suggestion_commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pr_comments_pr_idx": { + "name": "pr_comments_pr_idx", + "columns": [ + { + "expression": "pull_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pull_request_comments_pull_request_id_pull_requests_id_fk": { + "name": "pull_request_comments_pull_request_id_pull_requests_id_fk", + "tableFrom": "pull_request_comments", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pull_request_comments_review_id_pull_request_reviews_id_fk": { + "name": "pull_request_comments_review_id_pull_request_reviews_id_fk", + "tableFrom": "pull_request_comments", + "tableTo": "pull_request_reviews", + "columnsFrom": [ + "review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_request_comments_author_id_users_id_fk": { + "name": "pull_request_comments_author_id_users_id_fk", + "tableFrom": "pull_request_comments", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_request_comments_in_reply_to_id_pull_request_comments_id_fk": { + "name": "pull_request_comments_in_reply_to_id_pull_request_comments_id_fk", + "tableFrom": "pull_request_comments", + "tableTo": "pull_request_comments", + "columnsFrom": [ + "in_reply_to_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_request_comments_resolved_by_id_users_id_fk": { + "name": "pull_request_comments_resolved_by_id_users_id_fk", + "tableFrom": "pull_request_comments", + "tableTo": "users", + "columnsFrom": [ + "resolved_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_request_comments_suggestion_applied_by_id_users_id_fk": { + "name": "pull_request_comments_suggestion_applied_by_id_users_id_fk", + "tableFrom": "pull_request_comments", + "tableTo": "users", + "columnsFrom": [ + "suggestion_applied_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_request_labels": { + "name": "pull_request_labels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label_id": { + "name": "label_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "pull_request_labels_pull_request_id_pull_requests_id_fk": { + "name": "pull_request_labels_pull_request_id_pull_requests_id_fk", + "tableFrom": "pull_request_labels", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pull_request_labels_label_id_labels_id_fk": { + "name": "pull_request_labels_label_id_labels_id_fk", + "tableFrom": "pull_request_labels", + "tableTo": "labels", + "columnsFrom": [ + "label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_request_reviewers": { + "name": "pull_request_reviewers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pull_request_reviewers_pull_request_id_pull_requests_id_fk": { + "name": "pull_request_reviewers_pull_request_id_pull_requests_id_fk", + "tableFrom": "pull_request_reviewers", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pull_request_reviewers_user_id_users_id_fk": { + "name": "pull_request_reviewers_user_id_users_id_fk", + "tableFrom": "pull_request_reviewers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_request_reviews": { + "name": "pull_request_reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reviewer_id": { + "name": "reviewer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "submitted_at": { + "name": "submitted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "dismissed_by_id": { + "name": "dismissed_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dismissal_reason": { + "name": "dismissal_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pr_reviews_pr_idx": { + "name": "pr_reviews_pr_idx", + "columns": [ + { + "expression": "pull_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pull_request_reviews_pull_request_id_pull_requests_id_fk": { + "name": "pull_request_reviews_pull_request_id_pull_requests_id_fk", + "tableFrom": "pull_request_reviews", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pull_request_reviews_reviewer_id_users_id_fk": { + "name": "pull_request_reviews_reviewer_id_users_id_fk", + "tableFrom": "pull_request_reviews", + "tableTo": "users", + "columnsFrom": [ + "reviewer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_request_reviews_dismissed_by_id_users_id_fk": { + "name": "pull_request_reviews_dismissed_by_id_users_id_fk", + "tableFrom": "pull_request_reviews", + "tableTo": "users", + "columnsFrom": [ + "dismissed_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pull_requests": { + "name": "pull_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee_id": { + "name": "assignee_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "milestone_id": { + "name": "milestone_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "head_branch": { + "name": "head_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_repository_id": { + "name": "head_repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_branch": { + "name": "base_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_sha": { + "name": "base_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_draft": { + "name": "is_draft", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_merged": { + "name": "is_merged", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "merged_at": { + "name": "merged_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "merged_by_id": { + "name": "merged_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merge_commit_sha": { + "name": "merge_commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merge_sha": { + "name": "merge_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merge_method": { + "name": "merge_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "additions": { + "name": "additions", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "deletions": { + "name": "deletions", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "changed_files": { + "name": "changed_files", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "comment_count": { + "name": "comment_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "review_count": { + "name": "review_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "mergeable": { + "name": "mergeable", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "mergeable_state": { + "name": "mergeable_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rebaseable": { + "name": "rebaseable", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "maintainer_can_modify": { + "name": "maintainer_can_modify", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "allow_auto_merge": { + "name": "allow_auto_merge", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "auto_merge_method": { + "name": "auto_merge_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_merge_enabled_by_id": { + "name": "auto_merge_enabled_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_merge_enabled_at": { + "name": "auto_merge_enabled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "state_id": { + "name": "state_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_state_changed_at": { + "name": "custom_state_changed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "closed_by_id": { + "name": "closed_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "prs_repo_number_idx": { + "name": "prs_repo_number_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prs_repo_state_idx": { + "name": "prs_repo_state_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prs_author_idx": { + "name": "prs_author_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pull_requests_repository_id_repositories_id_fk": { + "name": "pull_requests_repository_id_repositories_id_fk", + "tableFrom": "pull_requests", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pull_requests_author_id_users_id_fk": { + "name": "pull_requests_author_id_users_id_fk", + "tableFrom": "pull_requests", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_requests_assignee_id_users_id_fk": { + "name": "pull_requests_assignee_id_users_id_fk", + "tableFrom": "pull_requests", + "tableTo": "users", + "columnsFrom": [ + "assignee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_requests_head_repository_id_repositories_id_fk": { + "name": "pull_requests_head_repository_id_repositories_id_fk", + "tableFrom": "pull_requests", + "tableTo": "repositories", + "columnsFrom": [ + "head_repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_requests_merged_by_id_users_id_fk": { + "name": "pull_requests_merged_by_id_users_id_fk", + "tableFrom": "pull_requests", + "tableTo": "users", + "columnsFrom": [ + "merged_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_requests_auto_merge_enabled_by_id_users_id_fk": { + "name": "pull_requests_auto_merge_enabled_by_id_users_id_fk", + "tableFrom": "pull_requests", + "tableTo": "users", + "columnsFrom": [ + "auto_merge_enabled_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_requests_state_id_pr_state_definitions_id_fk": { + "name": "pull_requests_state_id_pr_state_definitions_id_fk", + "tableFrom": "pull_requests", + "tableTo": "pr_state_definitions", + "columnsFrom": [ + "state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pull_requests_closed_by_id_users_id_fk": { + "name": "pull_requests_closed_by_id_users_id_fk", + "tableFrom": "pull_requests", + "tableTo": "users", + "columnsFrom": [ + "closed_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_cards": { + "name": "project_cards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "column_id": { + "name": "column_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_id": { + "name": "content_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "project_cards_column_id_project_columns_id_fk": { + "name": "project_cards_column_id_project_columns_id_fk", + "tableFrom": "project_cards", + "tableTo": "project_columns", + "columnsFrom": [ + "column_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_cards_creator_id_users_id_fk": { + "name": "project_cards_creator_id_users_id_fk", + "tableFrom": "project_cards", + "tableTo": "users", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project_columns": { + "name": "project_columns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_columns_project_id_projects_id_fk": { + "name": "project_columns_project_id_projects_id_fk", + "tableFrom": "project_columns", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.projects": { + "name": "projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "projects_repository_id_repositories_id_fk": { + "name": "projects_repository_id_repositories_id_fk", + "tableFrom": "projects", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "projects_creator_id_users_id_fk": { + "name": "projects_creator_id_users_id_fk", + "tableFrom": "projects", + "tableTo": "users", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scheduled_workflows": { + "name": "scheduled_workflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'UTC'" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "scheduled_workflows_workflow_id_workflows_id_fk": { + "name": "scheduled_workflows_workflow_id_workflows_id_fk", + "tableFrom": "scheduled_workflows", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_artifacts": { + "name": "workflow_artifacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "storage_path": { + "name": "storage_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_artifacts_run_id_workflow_runs_id_fk": { + "name": "workflow_artifacts_run_id_workflow_runs_id_fk", + "tableFrom": "workflow_artifacts", + "tableTo": "workflow_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_artifacts_job_id_workflow_jobs_id_fk": { + "name": "workflow_artifacts_job_id_workflow_jobs_id_fk", + "tableFrom": "workflow_artifacts", + "tableTo": "workflow_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_jobs": { + "name": "workflow_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "conclusion": { + "name": "conclusion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runner_id": { + "name": "runner_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runner_name": { + "name": "runner_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runner_group_id": { + "name": "runner_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runner_group_name": { + "name": "runner_group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "container_id": { + "name": "container_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "container_image": { + "name": "container_image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "needs": { + "name": "needs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environment_url": { + "name": "environment_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "matrix": { + "name": "matrix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_jobs_run_idx": { + "name": "workflow_jobs_run_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_jobs_run_id_workflow_runs_id_fk": { + "name": "workflow_jobs_run_id_workflow_runs_id_fk", + "tableFrom": "workflow_jobs", + "tableTo": "workflow_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_logs": { + "name": "workflow_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "step_id": { + "name": "step_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "log_level": { + "name": "log_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'info'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "line_number": { + "name": "line_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_logs_job_id_workflow_jobs_id_fk": { + "name": "workflow_logs_job_id_workflow_jobs_id_fk", + "tableFrom": "workflow_logs", + "tableTo": "workflow_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_logs_step_id_workflow_steps_id_fk": { + "name": "workflow_logs_step_id_workflow_steps_id_fk", + "tableFrom": "workflow_logs", + "tableTo": "workflow_steps", + "columnsFrom": [ + "step_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_runs": { + "name": "workflow_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_number": { + "name": "run_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "run_attempt": { + "name": "run_attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_title": { + "name": "display_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "conclusion": { + "name": "conclusion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_branch": { + "name": "head_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_branch": { + "name": "base_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_sha": { + "name": "base_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "triggered_by_id": { + "name": "triggered_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "workflow_config": { + "name": "workflow_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_runs_workflow_idx": { + "name": "workflow_runs_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_runs_repo_idx": { + "name": "workflow_runs_repo_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_runs_status_idx": { + "name": "workflow_runs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_runs_workflow_id_workflows_id_fk": { + "name": "workflow_runs_workflow_id_workflows_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_runs_repository_id_repositories_id_fk": { + "name": "workflow_runs_repository_id_repositories_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_runs_pull_request_id_pull_requests_id_fk": { + "name": "workflow_runs_pull_request_id_pull_requests_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_triggered_by_id_users_id_fk": { + "name": "workflow_runs_triggered_by_id_users_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "users", + "columnsFrom": [ + "triggered_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_secrets": { + "name": "workflow_secrets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_secrets_repository_id_repositories_id_fk": { + "name": "workflow_secrets_repository_id_repositories_id_fk", + "tableFrom": "workflow_secrets", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_secrets_created_by_id_users_id_fk": { + "name": "workflow_secrets_created_by_id_users_id_fk", + "tableFrom": "workflow_secrets", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_steps": { + "name": "workflow_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "conclusion": { + "name": "conclusion", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uses": { + "name": "uses", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "run": { + "name": "run", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shell": { + "name": "shell", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "working_directory": { + "name": "working_directory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "with": { + "name": "with", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "if": { + "name": "if", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "continue_on_error": { + "name": "continue_on_error", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "timeout_minutes": { + "name": "timeout_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_steps_job_id_workflow_jobs_id_fk": { + "name": "workflow_steps_job_id_workflow_jobs_id_fk", + "tableFrom": "workflow_steps", + "tableTo": "workflow_jobs", + "columnsFrom": [ + "job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_variables": { + "name": "workflow_variables", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_variables_repository_id_repositories_id_fk": { + "name": "workflow_variables_repository_id_repositories_id_fk", + "tableFrom": "workflow_variables", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_variables_created_by_id_users_id_fk": { + "name": "workflow_variables_created_by_id_users_id_fk", + "tableFrom": "workflow_variables", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflows": { + "name": "workflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "badge_url": { + "name": "badge_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflows_repo_idx": { + "name": "workflows_repo_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflows_repository_id_repositories_id_fk": { + "name": "workflows_repository_id_repositories_id_fk", + "tableFrom": "workflows", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wiki_pages": { + "name": "wiki_pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "format": { + "name": "format", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'markdown'" + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_editor_id": { + "name": "last_editor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "view_count": { + "name": "view_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wiki_pages_repository_id_repositories_id_fk": { + "name": "wiki_pages_repository_id_repositories_id_fk", + "tableFrom": "wiki_pages", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wiki_pages_parent_id_wiki_pages_id_fk": { + "name": "wiki_pages_parent_id_wiki_pages_id_fk", + "tableFrom": "wiki_pages", + "tableTo": "wiki_pages", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "wiki_pages_last_editor_id_users_id_fk": { + "name": "wiki_pages_last_editor_id_users_id_fk", + "tableFrom": "wiki_pages", + "tableTo": "users", + "columnsFrom": [ + "last_editor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wiki_revisions": { + "name": "wiki_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "page_id": { + "name": "page_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wiki_revisions_page_id_wiki_pages_id_fk": { + "name": "wiki_revisions_page_id_wiki_pages_id_fk", + "tableFrom": "wiki_revisions", + "tableTo": "wiki_pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wiki_revisions_author_id_users_id_fk": { + "name": "wiki_revisions_author_id_users_id_fk", + "tableFrom": "wiki_revisions", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_members": { + "name": "organization_members", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "custom_role_id": { + "name": "custom_role_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "organization_members_organization_id_user_id_pk": { + "name": "organization_members_organization_id_user_id_pk", + "columns": [ + "organization_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_verified": { + "name": "is_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_name_unique": { + "name": "organizations_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_roles": { + "name": "custom_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permissions": { + "name": "permissions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "custom_roles_organization_id_organizations_id_fk": { + "name": "custom_roles_organization_id_organizations_id_fk", + "tableFrom": "custom_roles", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_members": { + "name": "team_members", + "schema": "", + "columns": { + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_members_team_id_teams_id_fk": { + "name": "team_members_team_id_teams_id_fk", + "tableFrom": "team_members", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_members_user_id_users_id_fk": { + "name": "team_members_user_id_users_id_fk", + "tableFrom": "team_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "team_members_team_id_user_id_pk": { + "name": "team_members_team_id_user_id_pk", + "columns": [ + "team_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_repositories": { + "name": "team_repositories", + "schema": "", + "columns": { + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'read'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_repositories_team_id_teams_id_fk": { + "name": "team_repositories_team_id_teams_id_fk", + "tableFrom": "team_repositories", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_repositories_repository_id_repositories_id_fk": { + "name": "team_repositories_repository_id_repositories_id_fk", + "tableFrom": "team_repositories", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "team_repositories_team_id_repository_id_pk": { + "name": "team_repositories_team_id_repository_id_pk", + "columns": [ + "team_id", + "repository_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "privacy": { + "name": "privacy", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'visible'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_organization_id_organizations_id_fk": { + "name": "teams_organization_id_organizations_id_fk", + "tableFrom": "teams", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.activities": { + "name": "activities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ref_type": { + "name": "ref_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ref_name": { + "name": "ref_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "activities_user_idx": { + "name": "activities_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activities_repo_idx": { + "name": "activities_repo_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "activities_created_idx": { + "name": "activities_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activities_user_id_users_id_fk": { + "name": "activities_user_id_users_id_fk", + "tableFrom": "activities", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "activities_repository_id_repositories_id_fk": { + "name": "activities_repository_id_repositories_id_fk", + "tableFrom": "activities", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_logs": { + "name": "audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_ip": { + "name": "actor_ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_user_agent": { + "name": "actor_user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_name": { + "name": "target_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_logs_action_idx": { + "name": "audit_logs_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_actor_idx": { + "name": "audit_logs_actor_idx", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_created_idx": { + "name": "audit_logs_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_target_idx": { + "name": "audit_logs_target_idx", + "columns": [ + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_logs_user_id_users_id_fk": { + "name": "audit_logs_user_id_users_id_fk", + "tableFrom": "audit_logs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "audit_logs_repository_id_repositories_id_fk": { + "name": "audit_logs_repository_id_repositories_id_fk", + "tableFrom": "audit_logs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_archived": { + "name": "is_archived", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_user_read_idx": { + "name": "notifications_user_read_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_read", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notifications_user_idx": { + "name": "notifications_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notifications_repository_id_repositories_id_fk": { + "name": "notifications_repository_id_repositories_id_fk", + "tableFrom": "notifications", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "notifications_actor_id_users_id_fk": { + "name": "notifications_actor_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_scans": { + "name": "security_scans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "critical_count": { + "name": "critical_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "high_count": { + "name": "high_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "medium_count": { + "name": "medium_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "low_count": { + "name": "low_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "unknown_count": { + "name": "unknown_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "logs": { + "name": "logs", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "security_scans_repository_id_repositories_id_fk": { + "name": "security_scans_repository_id_repositories_id_fk", + "tableFrom": "security_scans", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_vulnerabilities": { + "name": "security_vulnerabilities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "scan_id": { + "name": "scan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vulnerability_id": { + "name": "vulnerability_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pkg_name": { + "name": "pkg_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "installed_version": { + "name": "installed_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fixed_version": { + "name": "fixed_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target": { + "name": "target", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "class": { + "name": "class", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "security_vulnerabilities_scan_id_security_scans_id_fk": { + "name": "security_vulnerabilities_scan_id_security_scans_id_fk", + "tableFrom": "security_vulnerabilities", + "tableTo": "security_scans", + "columnsFrom": [ + "scan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_policies": { + "name": "security_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enforcement_mode": { + "name": "enforcement_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'warn'" + }, + "secret_blocked_types": { + "name": "secret_blocked_types", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "secret_min_severity": { + "name": "secret_min_severity", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'HIGH'" + }, + "license_allowed_types": { + "name": "license_allowed_types", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'[\"permissive\"]'" + }, + "license_blocked_licenses": { + "name": "license_blocked_licenses", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_by_id": { + "name": "updated_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "security_policies_repository_id_repositories_id_fk": { + "name": "security_policies_repository_id_repositories_id_fk", + "tableFrom": "security_policies", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_policies_updated_by_id_users_id_fk": { + "name": "security_policies_updated_by_id_users_id_fk", + "tableFrom": "security_policies", + "tableTo": "users", + "columnsFrom": [ + "updated_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pipeline_runners": { + "name": "pipeline_runners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "os": { + "name": "os", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "arch": { + "name": "arch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'offline'" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pipeline_runners_repository_id_repositories_id_fk": { + "name": "pipeline_runners_repository_id_repositories_id_fk", + "tableFrom": "pipeline_runners", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_stack_entries": { + "name": "pr_stack_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "stack_id": { + "name": "stack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stack_order": { + "name": "stack_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_pr_id": { + "name": "parent_pr_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_stack_entries_stack_id_pr_stacks_id_fk": { + "name": "pr_stack_entries_stack_id_pr_stacks_id_fk", + "tableFrom": "pr_stack_entries", + "tableTo": "pr_stacks", + "columnsFrom": [ + "stack_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_stack_entries_pull_request_id_pull_requests_id_fk": { + "name": "pr_stack_entries_pull_request_id_pull_requests_id_fk", + "tableFrom": "pr_stack_entries", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_stack_entries_parent_pr_id_pull_requests_id_fk": { + "name": "pr_stack_entries_parent_pr_id_pull_requests_id_fk", + "tableFrom": "pr_stack_entries", + "tableTo": "pull_requests", + "columnsFrom": [ + "parent_pr_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_stacks": { + "name": "pr_stacks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_branch": { + "name": "base_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_stacks_repository_id_repositories_id_fk": { + "name": "pr_stacks_repository_id_repositories_id_fk", + "tableFrom": "pr_stacks", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_stacks_created_by_id_users_id_fk": { + "name": "pr_stacks_created_by_id_users_id_fk", + "tableFrom": "pr_stacks", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.merge_queue": { + "name": "merge_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stack_id": { + "name": "stack_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "ci_status": { + "name": "ci_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'pending'" + }, + "merge_method": { + "name": "merge_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'merge'" + }, + "delete_on_merge": { + "name": "delete_on_merge", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "execution_branch": { + "name": "execution_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "added_by_id": { + "name": "added_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "added_at": { + "name": "added_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "queued_at": { + "name": "queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "merge_queue_repository_id_repositories_id_fk": { + "name": "merge_queue_repository_id_repositories_id_fk", + "tableFrom": "merge_queue", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "merge_queue_pull_request_id_pull_requests_id_fk": { + "name": "merge_queue_pull_request_id_pull_requests_id_fk", + "tableFrom": "merge_queue", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "merge_queue_added_by_id_users_id_fk": { + "name": "merge_queue_added_by_id_users_id_fk", + "tableFrom": "merge_queue", + "tableTo": "users", + "columnsFrom": [ + "added_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.merge_queue_speculative_runs": { + "name": "merge_queue_speculative_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_ids": { + "name": "pull_request_ids", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_branch": { + "name": "base_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "ci_url": { + "name": "ci_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "merge_queue_speculative_runs_repository_id_repositories_id_fk": { + "name": "merge_queue_speculative_runs_repository_id_repositories_id_fk", + "tableFrom": "merge_queue_speculative_runs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_review_suggestions": { + "name": "ai_review_suggestions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ai_review_id": { + "name": "ai_review_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "line": { + "name": "line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "end_line": { + "name": "end_line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "suggested_fix": { + "name": "suggested_fix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_applied": { + "name": "is_applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_dismissed": { + "name": "is_dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "applied_by_id": { + "name": "applied_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "dismissed_by_id": { + "name": "dismissed_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dismiss_reason": { + "name": "dismiss_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ai_review_suggestions_ai_review_id_ai_reviews_id_fk": { + "name": "ai_review_suggestions_ai_review_id_ai_reviews_id_fk", + "tableFrom": "ai_review_suggestions", + "tableTo": "ai_reviews", + "columnsFrom": [ + "ai_review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_review_suggestions_applied_by_id_users_id_fk": { + "name": "ai_review_suggestions_applied_by_id_users_id_fk", + "tableFrom": "ai_review_suggestions", + "tableTo": "users", + "columnsFrom": [ + "applied_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ai_review_suggestions_dismissed_by_id_users_id_fk": { + "name": "ai_review_suggestions_dismissed_by_id_users_id_fk", + "tableFrom": "ai_review_suggestions", + "tableTo": "users", + "columnsFrom": [ + "dismissed_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_reviews": { + "name": "ai_reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stack_context": { + "name": "stack_context", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "includes_stack_context": { + "name": "includes_stack_context", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "overall_severity": { + "name": "overall_severity", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "suggestions_count": { + "name": "suggestions_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "tokens_used": { + "name": "tokens_used", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "prompt_tokens": { + "name": "prompt_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "completion_tokens": { + "name": "completion_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost_cents": { + "name": "cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "raw_response": { + "name": "raw_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "triggered_by_id": { + "name": "triggered_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ai_reviews_pull_request_id_pull_requests_id_fk": { + "name": "ai_reviews_pull_request_id_pull_requests_id_fk", + "tableFrom": "ai_reviews", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_reviews_triggered_by_id_users_id_fk": { + "name": "ai_reviews_triggered_by_id_users_id_fk", + "tableFrom": "ai_reviews", + "tableTo": "users", + "columnsFrom": [ + "triggered_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_metrics": { + "name": "pr_metrics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "time_to_first_review": { + "name": "time_to_first_review", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "time_to_approval": { + "name": "time_to_approval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "time_to_merge": { + "name": "time_to_merge", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_cycle_time": { + "name": "total_cycle_time", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "review_rounds": { + "name": "review_rounds", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "reviewers_count": { + "name": "reviewers_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "comments_count": { + "name": "comments_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "changes_requested_count": { + "name": "changes_requested_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "lines_added": { + "name": "lines_added", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "lines_removed": { + "name": "lines_removed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "files_changed": { + "name": "files_changed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "commits": { + "name": "commits", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "is_stacked": { + "name": "is_stacked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "stack_position": { + "name": "stack_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_created_at": { + "name": "pr_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "first_review_at": { + "name": "first_review_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "merged_at": { + "name": "merged_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_metrics_pull_request_id_pull_requests_id_fk": { + "name": "pr_metrics_pull_request_id_pull_requests_id_fk", + "tableFrom": "pr_metrics", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_metrics_repository_id_repositories_id_fk": { + "name": "pr_metrics_repository_id_repositories_id_fk", + "tableFrom": "pr_metrics", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_metrics_author_id_users_id_fk": { + "name": "pr_metrics_author_id_users_id_fk", + "tableFrom": "pr_metrics", + "tableTo": "users", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repo_metrics": { + "name": "repo_metrics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "week_of": { + "name": "week_of", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prs_opened": { + "name": "prs_opened", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "prs_merged": { + "name": "prs_merged", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "prs_closed": { + "name": "prs_closed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "avg_time_to_first_review": { + "name": "avg_time_to_first_review", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_time_to_merge": { + "name": "avg_time_to_merge", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_review_rounds": { + "name": "avg_review_rounds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_lines_changed": { + "name": "avg_lines_changed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_files_changed": { + "name": "avg_files_changed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "stacked_prs": { + "name": "stacked_prs", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "stacked_prs_percentage": { + "name": "stacked_prs_percentage", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "active_authors": { + "name": "active_authors", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "active_reviewers": { + "name": "active_reviewers", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "repo_metrics_repository_id_repositories_id_fk": { + "name": "repo_metrics_repository_id_repositories_id_fk", + "tableFrom": "repo_metrics", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.review_metrics": { + "name": "review_metrics", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "week_of": { + "name": "week_of", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prs_authored": { + "name": "prs_authored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "prs_authored_merged": { + "name": "prs_authored_merged", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "avg_time_to_merge_authored": { + "name": "avg_time_to_merge_authored", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lines_authored_added": { + "name": "lines_authored_added", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "lines_authored_removed": { + "name": "lines_authored_removed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "prs_reviewed": { + "name": "prs_reviewed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "avg_time_to_review": { + "name": "avg_time_to_review", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "comments_given": { + "name": "comments_given", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "approvals_given": { + "name": "approvals_given", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "changes_requested_given": { + "name": "changes_requested_given", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "review_metrics_user_id_users_id_fk": { + "name": "review_metrics_user_id_users_id_fk", + "tableFrom": "review_metrics", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "review_metrics_repository_id_repositories_id_fk": { + "name": "review_metrics_repository_id_repositories_id_fk", + "tableFrom": "review_metrics", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_channel_mappings": { + "name": "slack_channel_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel_name": { + "name": "channel_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notify_on": { + "name": "notify_on", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'[\"pr_created\",\"pr_merged\",\"review_requested\",\"ci_failed\"]'" + }, + "notify_branches": { + "name": "notify_branches", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notify_authors": { + "name": "notify_authors", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "slack_channel_mappings_workspace_id_slack_workspaces_id_fk": { + "name": "slack_channel_mappings_workspace_id_slack_workspaces_id_fk", + "tableFrom": "slack_channel_mappings", + "tableTo": "slack_workspaces", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_channel_mappings_repository_id_repositories_id_fk": { + "name": "slack_channel_mappings_repository_id_repositories_id_fk", + "tableFrom": "slack_channel_mappings", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_user_mappings": { + "name": "slack_user_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_user_id": { + "name": "slack_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_username": { + "name": "slack_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dm_preferences": { + "name": "dm_preferences", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'{\"review_requested\":true,\"pr_approved\":true,\"ci_failed\":true}'" + }, + "dnd_enabled": { + "name": "dnd_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "dnd_start": { + "name": "dnd_start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dnd_end": { + "name": "dnd_end", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dnd_timezone": { + "name": "dnd_timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "slack_user_mappings_user_id_users_id_fk": { + "name": "slack_user_mappings_user_id_users_id_fk", + "tableFrom": "slack_user_mappings", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_user_mappings_workspace_id_slack_workspaces_id_fk": { + "name": "slack_user_mappings_workspace_id_slack_workspaces_id_fk", + "tableFrom": "slack_user_mappings", + "tableTo": "slack_workspaces", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_workspaces": { + "name": "slack_workspaces", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team_domain": { + "name": "team_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bot_user_id": { + "name": "bot_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bot_access_token": { + "name": "bot_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "installed_by_id": { + "name": "installed_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "slack_workspaces_installed_by_id_users_id_fk": { + "name": "slack_workspaces_installed_by_id_users_id_fk", + "tableFrom": "slack_workspaces", + "tableTo": "users", + "columnsFrom": [ + "installed_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook_deliveries": { + "name": "webhook_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhook_id": { + "name": "webhook_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "response_code": { + "name": "response_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_headers": { + "name": "request_headers", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_headers": { + "name": "response_headers", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "webhook_deliveries_webhook_idx": { + "name": "webhook_deliveries_webhook_idx", + "columns": [ + { + "expression": "webhook_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_deliveries_created_idx": { + "name": "webhook_deliveries_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_deliveries_webhook_id_webhooks_id_fk": { + "name": "webhook_deliveries_webhook_id_webhooks_id_fk", + "tableFrom": "webhook_deliveries", + "tableTo": "webhooks", + "columnsFrom": [ + "webhook_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhooks": { + "name": "webhooks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'generic'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "events": { + "name": "events", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'json'" + }, + "delivery_count": { + "name": "delivery_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_delivery_status": { + "name": "last_delivery_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_delivery_at": { + "name": "last_delivery_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "webhooks_repo_idx": { + "name": "webhooks_repo_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhooks_repository_id_repositories_id_fk": { + "name": "webhooks_repository_id_repositories_id_fk", + "tableFrom": "webhooks", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhooks_created_by_id_users_id_fk": { + "name": "webhooks_created_by_id_users_id_fk", + "tableFrom": "webhooks", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.branch_protection": { + "name": "branch_protection", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pattern": { + "name": "pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "requires_pr": { + "name": "requires_pr", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "required_approvals": { + "name": "required_approvals", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "dismiss_stale_reviews": { + "name": "dismiss_stale_reviews", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "require_code_owner_reviews": { + "name": "require_code_owner_reviews", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "allow_force_pushes": { + "name": "allow_force_pushes", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "branch_protection_repository_id_repositories_id_fk": { + "name": "branch_protection_repository_id_repositories_id_fk", + "tableFrom": "branch_protection", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "branch_protection_created_by_id_users_id_fk": { + "name": "branch_protection_created_by_id_users_id_fk", + "tableFrom": "branch_protection", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_config": { + "name": "system_config", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_by_id": { + "name": "updated_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "system_config_updated_by_id_users_id_fk": { + "name": "system_config_updated_by_id_users_id_fk", + "tableFrom": "system_config", + "tableTo": "users", + "columnsFrom": [ + "updated_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inbox_sections": { + "name": "inbox_sections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filters": { + "name": "filters", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_collapsed": { + "name": "is_collapsed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "show_count": { + "name": "show_count", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "inbox_sections_user_id_users_id_fk": { + "name": "inbox_sections_user_id_users_id_fk", + "tableFrom": "inbox_sections", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shared_inbox_sections": { + "name": "shared_inbox_sections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "section_id": { + "name": "section_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_with_user_id": { + "name": "shared_with_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shared_with_team_id": { + "name": "shared_with_team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'view'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "shared_inbox_sections_section_id_inbox_sections_id_fk": { + "name": "shared_inbox_sections_section_id_inbox_sections_id_fk", + "tableFrom": "shared_inbox_sections", + "tableTo": "inbox_sections", + "columnsFrom": [ + "section_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shared_inbox_sections_shared_with_user_id_users_id_fk": { + "name": "shared_inbox_sections_shared_with_user_id_users_id_fk", + "tableFrom": "shared_inbox_sections", + "tableTo": "users", + "columnsFrom": [ + "shared_with_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.automation_executions": { + "name": "automation_executions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "rule_id": { + "name": "rule_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "triggered_by_id": { + "name": "triggered_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "triggered_by_type": { + "name": "triggered_by_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger_event": { + "name": "trigger_event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conditions_matched": { + "name": "conditions_matched", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "actions_executed": { + "name": "actions_executed", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "automation_executions_rule_id_automation_rules_id_fk": { + "name": "automation_executions_rule_id_automation_rules_id_fk", + "tableFrom": "automation_executions", + "tableTo": "automation_rules", + "columnsFrom": [ + "rule_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.automation_rules": { + "name": "automation_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conditions": { + "name": "conditions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actions": { + "name": "actions", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "automation_rules_repository_id_repositories_id_fk": { + "name": "automation_rules_repository_id_repositories_id_fk", + "tableFrom": "automation_rules", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "automation_rules_created_by_id_users_id_fk": { + "name": "automation_rules_created_by_id_users_id_fk", + "tableFrom": "automation_rules", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_codebase_context": { + "name": "ai_codebase_context", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "context_type": { + "name": "context_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "examples": { + "name": "examples", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "anti_patterns": { + "name": "anti_patterns", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_auto_detected": { + "name": "is_auto_detected", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ai_codebase_context_repository_id_repositories_id_fk": { + "name": "ai_codebase_context_repository_id_repositories_id_fk", + "tableFrom": "ai_codebase_context", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_review_rule_templates": { + "name": "ai_review_rule_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rule_type": { + "name": "rule_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_prompt": { + "name": "ai_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "regex_pattern": { + "name": "regex_pattern", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_globs": { + "name": "file_globs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "languages": { + "name": "languages", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_built_in": { + "name": "is_built_in", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "usage_count": { + "name": "usage_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_review_rules": { + "name": "ai_review_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rule_type": { + "name": "rule_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ai_prompt'" + }, + "ai_prompt": { + "name": "ai_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "regex_pattern": { + "name": "regex_pattern", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'info'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_globs": { + "name": "file_globs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "exclude_globs": { + "name": "exclude_globs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "languages": { + "name": "languages", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "is_auto_fix": { + "name": "is_auto_fix", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "match_count": { + "name": "match_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_match_at": { + "name": "last_match_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ai_review_rules_repository_id_repositories_id_fk": { + "name": "ai_review_rules_repository_id_repositories_id_fk", + "tableFrom": "ai_review_rules", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_review_rules_created_by_id_users_id_fk": { + "name": "ai_review_rules_created_by_id_users_id_fk", + "tableFrom": "ai_review_rules", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_digest_settings": { + "name": "email_digest_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "digest_type": { + "name": "digest_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "digest_time": { + "name": "digest_time", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'09:00'" + }, + "digest_day": { + "name": "digest_day", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "last_sent_at": { + "name": "last_sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "email_digest_settings_user_id_users_id_fk": { + "name": "email_digest_settings_user_id_users_id_fk", + "tableFrom": "email_digest_settings", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_preferences": { + "name": "notification_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_enabled": { + "name": "email_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "slack_enabled": { + "name": "slack_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "in_app_enabled": { + "name": "in_app_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "browser_push_enabled": { + "name": "browser_push_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_preferences_user_id_users_id_fk": { + "name": "notification_preferences_user_id_users_id_fk", + "tableFrom": "notification_preferences", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_preferences_repository_id_repositories_id_fk": { + "name": "notification_preferences_repository_id_repositories_id_fk", + "tableFrom": "notification_preferences", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification_quiet_hours": { + "name": "notification_quiet_hours", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "start_time": { + "name": "start_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'22:00'" + }, + "end_time": { + "name": "end_time", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'08:00'" + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "days_of_week": { + "name": "days_of_week", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0,1,2,3,4,5,6'" + }, + "allow_urgent": { + "name": "allow_urgent", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_quiet_hours_user_id_users_id_fk": { + "name": "notification_quiet_hours_user_id_users_id_fk", + "tableFrom": "notification_quiet_hours", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.push_subscriptions": { + "name": "push_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "p256dh_key": { + "name": "p256dh_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_key": { + "name": "auth_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "push_subscriptions_user_id_users_id_fk": { + "name": "push_subscriptions_user_id_users_id_fk", + "tableFrom": "push_subscriptions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sso_configs": { + "name": "sso_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'oidc'" + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'openid,profile,email'" + }, + "authorization_url": { + "name": "authorization_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_url": { + "name": "token_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userinfo_url": { + "name": "userinfo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "auto_create_users": { + "name": "auto_create_users", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "allowed_domains": { + "name": "allowed_domains", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_role": { + "name": "default_role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'member'" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sso_configs_organization_id_organizations_id_fk": { + "name": "sso_configs_organization_id_organizations_id_fk", + "tableFrom": "sso_configs", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repository_path_permissions": { + "name": "repository_path_permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path_pattern": { + "name": "path_pattern", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'write'" + }, + "require_approval": { + "name": "require_approval", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'false'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "repository_path_permissions_repository_id_repositories_id_fk": { + "name": "repository_path_permissions_repository_id_repositories_id_fk", + "tableFrom": "repository_path_permissions", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_path_permissions_user_id_users_id_fk": { + "name": "repository_path_permissions_user_id_users_id_fk", + "tableFrom": "repository_path_permissions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_path_permissions_team_id_teams_id_fk": { + "name": "repository_path_permissions_team_id_teams_id_fk", + "tableFrom": "repository_path_permissions", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_state_definitions": { + "name": "pr_state_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#6B7280'" + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_final": { + "name": "is_final", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "allow_merge": { + "name": "allow_merge", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "require_code_owner": { + "name": "require_code_owner", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_state_definitions_repository_id_repositories_id_fk": { + "name": "pr_state_definitions_repository_id_repositories_id_fk", + "tableFrom": "pr_state_definitions", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_state_reviewers": { + "name": "pr_state_reviewers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "state_definition_id": { + "name": "state_definition_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "required_count": { + "name": "required_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_state_reviewers_state_definition_id_pr_state_definitions_id_fk": { + "name": "pr_state_reviewers_state_definition_id_pr_state_definitions_id_fk", + "tableFrom": "pr_state_reviewers", + "tableTo": "pr_state_definitions", + "columnsFrom": [ + "state_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_state_reviewers_user_id_users_id_fk": { + "name": "pr_state_reviewers_user_id_users_id_fk", + "tableFrom": "pr_state_reviewers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_state_reviewers_team_id_teams_id_fk": { + "name": "pr_state_reviewers_team_id_teams_id_fk", + "tableFrom": "pr_state_reviewers", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_state_transitions": { + "name": "pr_state_transitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_state": { + "name": "from_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "to_state": { + "name": "to_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "changed_by_id": { + "name": "changed_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_state_transitions_changed_by_id_users_id_fk": { + "name": "pr_state_transitions_changed_by_id_users_id_fk", + "tableFrom": "pr_state_transitions", + "tableTo": "users", + "columnsFrom": [ + "changed_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_statuses": { + "name": "issue_statuses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#808080'" + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "is_default": { + "name": "is_default", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "issue_statuses_repository_id_repositories_id_fk": { + "name": "issue_statuses_repository_id_repositories_id_fk", + "tableFrom": "issue_statuses", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_field_definitions": { + "name": "custom_field_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "options": { + "name": "options", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "custom_field_definitions_repository_id_repositories_id_fk": { + "name": "custom_field_definitions_repository_id_repositories_id_fk", + "tableFrom": "custom_field_definitions", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_custom_field_values": { + "name": "issue_custom_field_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "issue_custom_field_values_issue_id_issues_id_fk": { + "name": "issue_custom_field_values_issue_id_issues_id_fk", + "tableFrom": "issue_custom_field_values", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_custom_field_values_field_id_custom_field_definitions_id_fk": { + "name": "issue_custom_field_values_field_id_custom_field_definitions_id_fk", + "tableFrom": "issue_custom_field_values", + "tableTo": "custom_field_definitions", + "columnsFrom": [ + "field_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auto_merge_rules": { + "name": "auto_merge_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "match_labels": { + "name": "match_labels", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "required_labels": { + "name": "required_labels", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "required_checks": { + "name": "required_checks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "min_approvals": { + "name": "min_approvals", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "require_code_owner": { + "name": "require_code_owner", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "allow_draft": { + "name": "allow_draft", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "min_time_in_queue_minutes": { + "name": "min_time_in_queue_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "merge_method": { + "name": "merge_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "match_count": { + "name": "match_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_matched_at": { + "name": "last_matched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_mismatch_reason": { + "name": "last_mismatch_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "auto_merge_rules_repository_id_repositories_id_fk": { + "name": "auto_merge_rules_repository_id_repositories_id_fk", + "tableFrom": "auto_merge_rules", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_merge_rules_created_by_id_users_id_fk": { + "name": "auto_merge_rules_created_by_id_users_id_fk", + "tableFrom": "auto_merge_rules", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.external_ci_integrations": { + "name": "external_ci_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "external_ci_integrations_repository_id_repositories_id_fk": { + "name": "external_ci_integrations_repository_id_repositories_id_fk", + "tableFrom": "external_ci_integrations", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "external_ci_integrations_created_by_id_users_id_fk": { + "name": "external_ci_integrations_created_by_id_users_id_fk", + "tableFrom": "external_ci_integrations", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.review_requirements": { + "name": "review_requirements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "min_approvals": { + "name": "min_approvals", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "require_code_owner": { + "name": "require_code_owner", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "require_team_lead": { + "name": "require_team_lead", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "dismiss_stale_reviews": { + "name": "dismiss_stale_reviews", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "require_rereview_on_push": { + "name": "require_rereview_on_push", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "review_requirements_repository_id_repositories_id_fk": { + "name": "review_requirements_repository_id_repositories_id_fk", + "tableFrom": "review_requirements", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "review_requirements_created_by_id_users_id_fk": { + "name": "review_requirements_created_by_id_users_id_fk", + "tableFrom": "review_requirements", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reviewer_rules": { + "name": "reviewer_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rule_type": { + "name": "rule_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "path_pattern": { + "name": "path_pattern", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "reviewer_rules_repository_id_repositories_id_fk": { + "name": "reviewer_rules_repository_id_repositories_id_fk", + "tableFrom": "reviewer_rules", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reviewer_rules_created_by_id_users_id_fk": { + "name": "reviewer_rules_created_by_id_users_id_fk", + "tableFrom": "reviewer_rules", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_change_detections": { + "name": "api_change_detections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "breaking": { + "name": "breaking", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "affected_files": { + "name": "affected_files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "api_change_detections_pull_request_id_pull_requests_id_fk": { + "name": "api_change_detections_pull_request_id_pull_requests_id_fk", + "tableFrom": "api_change_detections", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.breaking_changes": { + "name": "breaking_changes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_type": { + "name": "change_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "affected_files": { + "name": "affected_files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "suggested_action": { + "name": "suggested_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acknowledged": { + "name": "acknowledged", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "breaking_changes_pull_request_id_pull_requests_id_fk": { + "name": "breaking_changes_pull_request_id_pull_requests_id_fk", + "tableFrom": "breaking_changes", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.change_set_items": { + "name": "change_set_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "change_set_id": { + "name": "change_set_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "change_set_items_change_set_id_change_sets_id_fk": { + "name": "change_set_items_change_set_id_change_sets_id_fk", + "tableFrom": "change_set_items", + "tableTo": "change_sets", + "columnsFrom": [ + "change_set_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "change_set_items_pull_request_id_pull_requests_id_fk": { + "name": "change_set_items_pull_request_id_pull_requests_id_fk", + "tableFrom": "change_set_items", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "change_set_items_repository_id_repositories_id_fk": { + "name": "change_set_items_repository_id_repositories_id_fk", + "tableFrom": "change_set_items", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.change_sets": { + "name": "change_sets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "change_sets_created_by_id_users_id_fk": { + "name": "change_sets_created_by_id_users_id_fk", + "tableFrom": "change_sets", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_integrations": { + "name": "chat_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "webhook_url": { + "name": "webhook_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_token": { + "name": "api_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "events": { + "name": "events", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_configs": { + "name": "cloud_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credentials": { + "name": "credentials", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "cloud_configs_repository_id_repositories_id_fk": { + "name": "cloud_configs_repository_id_repositories_id_fk", + "tableFrom": "cloud_configs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_quality_configs": { + "name": "code_quality_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_key": { + "name": "project_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_token": { + "name": "api_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "server_url": { + "name": "server_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "webhook_secret": { + "name": "webhook_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "report_on_pr": { + "name": "report_on_pr", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "block_on_fail": { + "name": "block_on_fail", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "min_coverage": { + "name": "min_coverage", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "code_quality_configs_repository_id_repositories_id_fk": { + "name": "code_quality_configs_repository_id_repositories_id_fk", + "tableFrom": "code_quality_configs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_quality_issues": { + "name": "code_quality_issues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_type": { + "name": "issue_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file": { + "name": "file", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line": { + "name": "line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rule": { + "name": "rule", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "effort": { + "name": "effort", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'open'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "code_quality_issues_repository_id_repositories_id_fk": { + "name": "code_quality_issues_repository_id_repositories_id_fk", + "tableFrom": "code_quality_issues", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.coverage_reports": { + "name": "coverage_reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "coverage": { + "name": "coverage", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "lines_covered": { + "name": "lines_covered", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "lines_total": { + "name": "lines_total", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "branch_coverage": { + "name": "branch_coverage", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "delta": { + "name": "delta", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "report_url": { + "name": "report_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "coverage_reports_repository_id_repositories_id_fk": { + "name": "coverage_reports_repository_id_repositories_id_fk", + "tableFrom": "coverage_reports", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cross_repo_issue_links": { + "name": "cross_repo_issue_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "source_issue_id": { + "name": "source_issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_issue_id": { + "name": "target_issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "link_type": { + "name": "link_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "cross_repo_issue_links_source_issue_id_issues_id_fk": { + "name": "cross_repo_issue_links_source_issue_id_issues_id_fk", + "tableFrom": "cross_repo_issue_links", + "tableTo": "issues", + "columnsFrom": [ + "source_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cross_repo_issue_links_target_issue_id_issues_id_fk": { + "name": "cross_repo_issue_links_target_issue_id_issues_id_fk", + "tableFrom": "cross_repo_issue_links", + "tableTo": "issues", + "columnsFrom": [ + "target_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_dashboards": { + "name": "custom_dashboards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "layout": { + "name": "layout", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "custom_dashboards_user_id_users_id_fk": { + "name": "custom_dashboards_user_id_users_id_fk", + "tableFrom": "custom_dashboards", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dashboard_widgets": { + "name": "dashboard_widgets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "dashboard_id": { + "name": "dashboard_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "widget_type": { + "name": "widget_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "position": { + "name": "position", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "dashboard_widgets_dashboard_id_custom_dashboards_id_fk": { + "name": "dashboard_widgets_dashboard_id_custom_dashboards_id_fk", + "tableFrom": "dashboard_widgets", + "tableTo": "custom_dashboards", + "columnsFrom": [ + "dashboard_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_deployments": { + "name": "cloud_deployments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logs": { + "name": "logs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "cloud_deployments_config_id_cloud_configs_id_fk": { + "name": "cloud_deployments_config_id_cloud_configs_id_fk", + "tableFrom": "cloud_deployments", + "tableTo": "cloud_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_settings": { + "name": "email_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "smtp_host": { + "name": "smtp_host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "smtp_port": { + "name": "smtp_port", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "smtp_user": { + "name": "smtp_user", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "smtp_pass": { + "name": "smtp_pass", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_address": { + "name": "from_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.external_builds": { + "name": "external_builds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_build_id": { + "name": "external_build_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "build_number": { + "name": "build_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "external_builds_config_id_external_ci_configs_id_fk": { + "name": "external_builds_config_id_external_ci_configs_id_fk", + "tableFrom": "external_builds", + "tableTo": "external_ci_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.external_ci_configs": { + "name": "external_ci_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "api_token": { + "name": "api_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "webhook_secret": { + "name": "webhook_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "sync_status": { + "name": "sync_status", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "external_ci_configs_repository_id_repositories_id_fk": { + "name": "external_ci_configs_repository_id_repositories_id_fk", + "tableFrom": "external_ci_configs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_approvals": { + "name": "file_approvals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approved_by_id": { + "name": "approved_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "file_approvals_pull_request_id_pull_requests_id_fk": { + "name": "file_approvals_pull_request_id_pull_requests_id_fk", + "tableFrom": "file_approvals", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "file_approvals_approved_by_id_users_id_fk": { + "name": "file_approvals_approved_by_id_users_id_fk", + "tableFrom": "file_approvals", + "tableTo": "users", + "columnsFrom": [ + "approved_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_hotspots": { + "name": "file_hotspots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_count": { + "name": "change_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "bug_count": { + "name": "bug_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "review_count": { + "name": "review_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "complexity_score": { + "name": "complexity_score", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "last_modified": { + "name": "last_modified", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "file_hotspots_repository_id_repositories_id_fk": { + "name": "file_hotspots_repository_id_repositories_id_fk", + "tableFrom": "file_hotspots", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ip_allow_lists": { + "name": "ip_allow_lists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cidr_block": { + "name": "cidr_block", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_tracker_configs": { + "name": "issue_tracker_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "api_url": { + "name": "api_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_token": { + "name": "api_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_key": { + "name": "project_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "webhook_secret": { + "name": "webhook_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "sync_to_external": { + "name": "sync_to_external", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "sync_from_external": { + "name": "sync_from_external", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "issue_tracker_configs_repository_id_repositories_id_fk": { + "name": "issue_tracker_configs_repository_id_repositories_id_fk", + "tableFrom": "issue_tracker_configs", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.issue_tracker_links": { + "name": "issue_tracker_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "local_issue_id": { + "name": "local_issue_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_key": { + "name": "external_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_url": { + "name": "external_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "issue_tracker_links_config_id_issue_tracker_configs_id_fk": { + "name": "issue_tracker_links_config_id_issue_tracker_configs_id_fk", + "tableFrom": "issue_tracker_links", + "tableTo": "issue_tracker_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "issue_tracker_links_local_issue_id_issues_id_fk": { + "name": "issue_tracker_links_local_issue_id_issues_id_fk", + "tableFrom": "issue_tracker_links", + "tableTo": "issues", + "columnsFrom": [ + "local_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.license_scans": { + "name": "license_scans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "package_name": { + "name": "package_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "package_version": { + "name": "package_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "license": { + "name": "license", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "license_type": { + "name": "license_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_compliant": { + "name": "is_compliant", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "policy_violation": { + "name": "policy_violation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "license_scans_repository_id_repositories_id_fk": { + "name": "license_scans_repository_id_repositories_id_fk", + "tableFrom": "license_scans", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.merge_gates": { + "name": "merge_gates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gate_type": { + "name": "gate_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "condition_script": { + "name": "condition_script", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "merge_gates_repository_id_repositories_id_fk": { + "name": "merge_gates_repository_id_repositories_id_fk", + "tableFrom": "merge_gates", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metric_snapshots": { + "name": "metric_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metric_type": { + "name": "metric_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "dimensions": { + "name": "dimensions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "metric_snapshots_repository_id_repositories_id_fk": { + "name": "metric_snapshots_repository_id_repositories_id_fk", + "tableFrom": "metric_snapshots", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_detections": { + "name": "migration_detections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "migration_type": { + "name": "migration_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool": { + "name": "tool", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_reversible": { + "name": "is_reversible", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "requires_downtime": { + "name": "requires_downtime", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "migration_detections_pull_request_id_pull_requests_id_fk": { + "name": "migration_detections_pull_request_id_pull_requests_id_fk", + "tableFrom": "migration_detections", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_issue_links": { + "name": "pr_issue_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "link_type": { + "name": "link_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pr_issue_links_pull_request_id_pull_requests_id_fk": { + "name": "pr_issue_links_pull_request_id_pull_requests_id_fk", + "tableFrom": "pr_issue_links", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_issue_links_issue_id_issues_id_fk": { + "name": "pr_issue_links_issue_id_issues_id_fk", + "tableFrom": "pr_issue_links", + "tableTo": "issues", + "columnsFrom": [ + "issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limit_logs": { + "name": "rate_limit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "rule_id": { + "name": "rule_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "window_start": { + "name": "window_start", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "blocked": { + "name": "blocked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "rate_limit_logs_rule_id_rate_limit_rules_id_fk": { + "name": "rate_limit_logs_rule_id_rate_limit_rules_id_fk", + "tableFrom": "rate_limit_logs", + "tableTo": "rate_limit_rules", + "columnsFrom": [ + "rule_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limit_rules": { + "name": "rate_limit_rules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "window_ms": { + "name": "window_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "max_requests": { + "name": "max_requests", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "key_type": { + "name": "key_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'ip'" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.repository_workflows": { + "name": "repository_workflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "repository_workflows_repository_id_repositories_id_fk": { + "name": "repository_workflows_repository_id_repositories_id_fk", + "tableFrom": "repository_workflows", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "repository_workflows_template_id_workflow_templates_id_fk": { + "name": "repository_workflows_template_id_workflow_templates_id_fk", + "tableFrom": "repository_workflows", + "tableTo": "workflow_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.required_status_checks": { + "name": "required_status_checks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "check_name": { + "name": "check_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_required": { + "name": "is_required", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "strict_mode": { + "name": "strict_mode", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "path_filter": { + "name": "path_filter", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "required_status_checks_repository_id_repositories_id_fk": { + "name": "required_status_checks_repository_id_repositories_id_fk", + "tableFrom": "required_status_checks", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.review_templates": { + "name": "review_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "review_templates_repository_id_repositories_id_fk": { + "name": "review_templates_repository_id_repositories_id_fk", + "tableFrom": "review_templates", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.saml_configs": { + "name": "saml_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sso_url": { + "name": "sso_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificate": { + "name": "certificate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature_algorithm": { + "name": "signature_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'RSA-SHA256'" + }, + "digest_algorithm": { + "name": "digest_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'SHA256'" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.secret_scan_results": { + "name": "secret_scan_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "commit_sha": { + "name": "commit_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_type": { + "name": "secret_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file": { + "name": "file", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "line": { + "name": "line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "snippet": { + "name": "snippet", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'open'" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "resolved_by_id": { + "name": "resolved_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "secret_scan_results_repository_id_repositories_id_fk": { + "name": "secret_scan_results_repository_id_repositories_id_fk", + "tableFrom": "secret_scan_results", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_states": { + "name": "workflow_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'#6b7280'" + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_order": { + "name": "display_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_closed_state": { + "name": "is_closed_state", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_states_repository_id_repositories_id_fk": { + "name": "workflow_states_repository_id_repositories_id_fk", + "tableFrom": "workflow_states", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_templates": { + "name": "workflow_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_official": { + "name": "is_official", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "downloads": { + "name": "downloads", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_transitions": { + "name": "workflow_transitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_state_id": { + "name": "from_state_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "to_state_id": { + "name": "to_state_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requires_comment": { + "name": "requires_comment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workflow_transitions_repository_id_repositories_id_fk": { + "name": "workflow_transitions_repository_id_repositories_id_fk", + "tableFrom": "workflow_transitions", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_transitions_from_state_id_workflow_states_id_fk": { + "name": "workflow_transitions_from_state_id_workflow_states_id_fk", + "tableFrom": "workflow_transitions", + "tableTo": "workflow_states", + "columnsFrom": [ + "from_state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_transitions_to_state_id_workflow_states_id_fk": { + "name": "workflow_transitions_to_state_id_workflow_states_id_fk", + "tableFrom": "workflow_transitions", + "tableTo": "workflow_states", + "columnsFrom": [ + "to_state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.package_versions": { + "name": "package_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "package_id": { + "name": "package_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "digest": { + "name": "digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "storage_path": { + "name": "storage_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "git_ref": { + "name": "git_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "published_by_id": { + "name": "published_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "yanked": { + "name": "yanked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pkg_version_idx": { + "name": "pkg_version_idx", + "columns": [ + { + "expression": "package_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pkg_digest_idx": { + "name": "pkg_digest_idx", + "columns": [ + { + "expression": "digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "package_versions_package_id_packages_id_fk": { + "name": "package_versions_package_id_packages_id_fk", + "tableFrom": "package_versions", + "tableTo": "packages", + "columnsFrom": [ + "package_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "package_versions_published_by_id_users_id_fk": { + "name": "package_versions_published_by_id_users_id_fk", + "tableFrom": "package_versions", + "tableTo": "users", + "columnsFrom": [ + "published_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.packages": { + "name": "packages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "download_count": { + "name": "download_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_id": { + "name": "created_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pkg_org_type_name_idx": { + "name": "pkg_org_type_name_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pkg_type_idx": { + "name": "pkg_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "packages_organization_id_organizations_id_fk": { + "name": "packages_organization_id_organizations_id_fk", + "tableFrom": "packages", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "packages_repository_id_repositories_id_fk": { + "name": "packages_repository_id_repositories_id_fk", + "tableFrom": "packages", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "packages_created_by_id_users_id_fk": { + "name": "packages_created_by_id_users_id_fk", + "tableFrom": "packages", + "tableTo": "users", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_inline_comments": { + "name": "ai_inline_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "analysis_id": { + "name": "analysis_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "line": { + "name": "line", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_line": { + "name": "end_line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "side": { + "name": "side", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'right'" + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "suggested_fix": { + "name": "suggested_fix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_resolved": { + "name": "is_resolved", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "is_applied": { + "name": "is_applied", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_inline_comments_analysis_idx": { + "name": "ai_inline_comments_analysis_idx", + "columns": [ + { + "expression": "analysis_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ai_inline_comments_pr_idx": { + "name": "ai_inline_comments_pr_idx", + "columns": [ + { + "expression": "pull_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ai_inline_comments_path_idx": { + "name": "ai_inline_comments_path_idx", + "columns": [ + { + "expression": "file_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ai_inline_comments_severity_idx": { + "name": "ai_inline_comments_severity_idx", + "columns": [ + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_inline_comments_analysis_id_pr_review_analyses_id_fk": { + "name": "ai_inline_comments_analysis_id_pr_review_analyses_id_fk", + "tableFrom": "ai_inline_comments", + "tableTo": "pr_review_analyses", + "columnsFrom": [ + "analysis_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_inline_comments_pull_request_id_pull_requests_id_fk": { + "name": "ai_inline_comments_pull_request_id_pull_requests_id_fk", + "tableFrom": "ai_inline_comments", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_review_threads": { + "name": "ai_review_threads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "analysis_id": { + "name": "analysis_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line": { + "name": "line", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "messages": { + "name": "messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_thread_analysis_idx": { + "name": "ai_thread_analysis_idx", + "columns": [ + { + "expression": "analysis_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ai_thread_pr_idx": { + "name": "ai_thread_pr_idx", + "columns": [ + { + "expression": "pull_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_review_threads_analysis_id_pr_review_analyses_id_fk": { + "name": "ai_review_threads_analysis_id_pr_review_analyses_id_fk", + "tableFrom": "ai_review_threads", + "tableTo": "pr_review_analyses", + "columnsFrom": [ + "analysis_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_review_threads_pull_request_id_pull_requests_id_fk": { + "name": "ai_review_threads_pull_request_id_pull_requests_id_fk", + "tableFrom": "ai_review_threads", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_summaries": { + "name": "file_summaries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "analysis_id": { + "name": "analysis_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "risk_level": { + "name": "risk_level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "additions": { + "name": "additions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deletions": { + "name": "deletions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "complexity_score": { + "name": "complexity_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "file_summaries_analysis_idx": { + "name": "file_summaries_analysis_idx", + "columns": [ + { + "expression": "analysis_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "file_summaries_path_idx": { + "name": "file_summaries_path_idx", + "columns": [ + { + "expression": "file_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_summaries_analysis_id_pr_review_analyses_id_fk": { + "name": "file_summaries_analysis_id_pr_review_analyses_id_fk", + "tableFrom": "file_summaries", + "tableTo": "pr_review_analyses", + "columnsFrom": [ + "analysis_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pr_review_analyses": { + "name": "pr_review_analyses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "pull_request_id": { + "name": "pull_request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "complexity_data": { + "name": "complexity_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dependency_graph": { + "name": "dependency_graph", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "architecture_impact": { + "name": "architecture_impact", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "change_groups": { + "name": "change_groups", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "blast_radius": { + "name": "blast_radius", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "health_score": { + "name": "health_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "health_grade": { + "name": "health_grade", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "summaries": { + "name": "summaries", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "base_sha": { + "name": "base_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "files_analyzed": { + "name": "files_analyzed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "chunks_processed": { + "name": "chunks_processed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_additions": { + "name": "total_additions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_deletions": { + "name": "total_deletions", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tokens_used": { + "name": "tokens_used", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost_cents": { + "name": "cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "raw_ai_response": { + "name": "raw_ai_response", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "triggered_by_id": { + "name": "triggered_by_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pr_analysis_pr_idx": { + "name": "pr_analysis_pr_idx", + "columns": [ + { + "expression": "pull_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pr_analysis_repo_idx": { + "name": "pr_analysis_repo_idx", + "columns": [ + { + "expression": "repository_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pr_analysis_status_idx": { + "name": "pr_analysis_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pr_analysis_created_idx": { + "name": "pr_analysis_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pr_review_analyses_pull_request_id_pull_requests_id_fk": { + "name": "pr_review_analyses_pull_request_id_pull_requests_id_fk", + "tableFrom": "pr_review_analyses", + "tableTo": "pull_requests", + "columnsFrom": [ + "pull_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_review_analyses_repository_id_repositories_id_fk": { + "name": "pr_review_analyses_repository_id_repositories_id_fk", + "tableFrom": "pr_review_analyses", + "tableTo": "repositories", + "columnsFrom": [ + "repository_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pr_review_analyses_triggered_by_id_users_id_fk": { + "name": "pr_review_analyses_triggered_by_id_users_id_fk", + "tableFrom": "pr_review_analyses", + "tableTo": "users", + "columnsFrom": [ + "triggered_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 00000000..94b528ef --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1785662563617, + "tag": "0000_jittery_bedlam", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/features.md b/features.md index 79d3de0b..b42a4404 100644 --- a/features.md +++ b/features.md @@ -5,7 +5,7 @@ Canonical source: `doc/feature_audit.md` ## Recently completed - Local free-model AI support (`local` provider, Ollama/OpenAI-compatible) with configurable `localBaseUrl` and key. -- External-agent (CodeRabbit-like) callback and async AI review flow documented and supported. +- External-agent (Greptile-like) callback and async AI review flow documented and supported. - PR dependency APIs: - `GET /api/repos/{owner}/{repo}/pulls/dependencies` - `POST /api/repos/{owner}/{repo}/pulls/stack-order` diff --git a/make-admin.ts b/make-admin.ts new file mode 100644 index 00000000..00b0eb16 --- /dev/null +++ b/make-admin.ts @@ -0,0 +1,8 @@ +import { getDatabase, schema } from "./src/db"; +async function main() { + const db = getDatabase(); + await db.update(schema.users).set({ isAdmin: true }); + console.log("Made all users admin."); + process.exit(0); +} +main(); diff --git a/notimplemented.md b/notimplemented.md index e017a76f..9e93e3d7 100644 --- a/notimplemented.md +++ b/notimplemented.md @@ -50,10 +50,8 @@ Ship OpenCodeHub to a production-grade state by closing all partial (`⚠️`) a - Kubernetes deployment packaging - Horizontal scaling hardening -## Missing (46) +## Missing (44) - File-level permissions -- Custom PR states -- Required reviewers per PR state - PR checks completion - Suggested changes - Custom issue fields @@ -203,7 +201,6 @@ Acceptance criteria: - Integration contract tests per provider. ## WS3 Security + Compliance -- Complete CODEOWNERS policy enforcement. - Harden secret scanning and compliance scanning. - Complete RBAC admin and audit flows. Acceptance criteria: @@ -340,13 +337,7 @@ Mitigation: 1. Finish Phase 0 baseline stabilization. 2. Convert every `⚠️`/`❌` item into tracked issue with owner and estimate. -3. Start Phase 1 with this order: - - Custom PR states - - Required reviewers per state - - CODEOWNERS enforcement - - Suggested changes - - Partial file approvals -4. Enable docs parity workflow in CI for both docs trees. +3. Enable docs parity workflow in CI for both docs trees. ## 10. Definition Of Done Per Feature - Code complete + migrations + backfill scripts @@ -357,3 +348,10 @@ Mitigation: - Changelog updated - Rollback plan documented +## 11. Recently Implemented +- ✅ Fix baseline TS errors (Phase 0) +- ✅ Custom PR States (Phase 1) +- ✅ PR Reviewers assigned by Custom State +- ✅ CODEOWNERS Enforcement (Phase 1) +- ✅ Suggested changes (Phase 1) +- ✅ Partial file approvals (Phase 1) diff --git a/out.json b/out.json new file mode 100644 index 00000000..e69de29b diff --git a/package.json b/package.json index 27e70420..8c371683 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,11 @@ }, "scripts": { "dev": "astro dev", - "start": "astro dev", "build": "astro build", + "start": "node ./dist/server/entry.mjs", + "start:ssh": "bun scripts/ssh-server.ts", + "start:worker": "bun scripts/worker.ts", + "migrate": "bun scripts/migrate.ts", "preview": "astro preview", "astro": "astro", "db:generate": "drizzle-kit generate", @@ -47,7 +50,6 @@ "@astrojs/check": "^0.9.6", "@astrojs/node": "^8.3.0", "@astrojs/react": "^3.6.0", - "@astrojs/tailwind": "^5.1.0", "@astrojs/vercel": "^7.7.0", "@aws-sdk/client-s3": "^3.948.0", "@aws-sdk/lib-storage": "^3.972.0", @@ -55,7 +57,11 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@google/genai": "^2.11.0", + "@iconify-json/file-icons": "^1.2.2", + "@iconify-json/vscode-icons": "^1.2.67", "@libsql/client": "^0.15.15", + "@mistralai/mistralai": "^2.4.1", "@monaco-editor/react": "^4.6.0", "@opentelemetry/auto-instrumentations-node": "^0.76.0", "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", @@ -90,6 +96,7 @@ "@upstash/redis": "^1.36.0", "arctic": "^3.7.0", "astro": "^4.11.0", + "astro-icon": "^1.1.5", "bcryptjs": "^2.4.3", "better-sqlite3": "^11.1.2", "bullmq": "^5.65.1", @@ -99,11 +106,13 @@ "cobe": "^0.6.5", "date-fns": "^4.1.0", "dockerode": "^4.0.12", + "dotenv": "^17.4.2", "drizzle-orm": "^0.45.2", "framer-motion": "^11.18.2", "github-markdown-css": "^5.8.1", "graphql": "^16.12.0", "graphql-yoga": "^5.18.0", + "highlight.js": "^11.11.1", "ioredis": "^5.9.2", "isomorphic-dompurify": "^2.35.0", "isomorphic-git": "^1.25.10", @@ -132,11 +141,13 @@ "react-dom": "^18.3.1", "react-globe.gl": "^2.37.0", "recharts": "^3.7.0", + "rehype-highlight": "^7.0.2", "rehype-sanitize": "^6.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", + "shiki": "^1.29.0", "simple-git": "^3.36.0", "sonner": "^2.0.7", "ssh2": "^1.15.0", @@ -145,10 +156,12 @@ "three": "^0.182.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", - "shiki": "^1.29.0", "yaml": "^2.8.0", "zod": "^3.23.8", - "zustand": "^4.5.4" + "zustand": "^4.5.4", + "autoprefixer": "^10.4.19", + "postcss": "^8.5.2", + "tailwindcss": "^3.4.4" }, "devDependencies": { "@axe-core/playwright": "^4.10.0", @@ -168,12 +181,10 @@ "@types/ssh2": "^1.15.0", "@types/three": "^0.182.0", "@vitest/coverage-v8": "^4.1.5", - "autoprefixer": "^10.4.19", "drizzle-kit": "^0.31.8", "pino-pretty": "^13.1.3", - "postcss": "^8.5.10", - "tailwindcss": "^3.4.4", "typescript": "^5.9.3", + "vite": "^5.4.11", "vitest": "^4.1.5" }, "engines": { @@ -182,20 +193,19 @@ "overrides": { "fast-xml-parser": "^5.8.0", "fast-xml-builder": "^1.2.0", - "fast-uri": "^3.1.2", + "fast-uri": "^3.0.6", "protobufjs": "^7.6.0", - "tar": "^7.5.16", + "tar": "^7.5.8", "rollup": "^4.61.0", "picomatch": "^4.0.4", "picomatch@2": "^2.3.2", "qs": "^6.15.0", "ws": "^8.18.0", - "yaml": "^2.8.0", "devalue": "^5.8.1", "lodash": "^4.17.23", "lodash-es": "^4.17.23", "preact": "^10.29.0", - "brace-expansion": "^2.1.1", + "brace-expansion": "^2.0.1", "diff": "^5.2.0", "cross-spawn": "^7.0.6" } diff --git a/playwright-report/index.html b/playwright-report/index.html index 60ec925d..d4cddebf 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -7,7 +7,7 @@ <meta name='color-scheme' content='dark light'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>Playwright Test Report - -
- \ No newline at end of file + \ No newline at end of file diff --git a/protection.json b/protection.json new file mode 100644 index 00000000..15d1d528 --- /dev/null +++ b/protection.json @@ -0,0 +1,27 @@ +{ + "required_status_checks": { + "strict": true, + "contexts": [ + "Quality Gate", + "Lint & Type Check", + "Docs Parity", + "Security Gates", + "Unit & Integration Tests", + "E2E Tests (Playwright)", + "Container Security (Trivy)", + "SAST (Semgrep)", + "Performance Gate", + "Build" + ] + }, + "enforce_admins": false, + "required_pull_request_reviews": { + "dismiss_stale_reviews": true, + "require_code_owner_reviews": false, + "required_approving_review_count": 1 + }, + "restrictions": null, + "required_linear_history": true, + "allow_force_pushes": false, + "allow_deletions": false +} diff --git a/render.yaml b/render.yaml new file mode 100644 index 00000000..4833d4fc --- /dev/null +++ b/render.yaml @@ -0,0 +1,108 @@ +# OpenCodeHub Render Blueprint +# Deploy with: New → Blueprint → Select this repo +# +# Prerequisites: +# 1. Create a free Upstash Redis at https://upstash.com (region: Asia Pacific) +# 2. Copy the Redis URL +# +# After deploy: +# 1. Set REDIS_URL from Upstash in Environment tab +# 2. Update SITE_URL with your actual Render URL +# 3. Visit your URL → /setup to create admin account + +services: + # ── Web Service (App + API + Git HTTP) ────────────────────── + - type: web + name: opencodehub + runtime: image + image: + url: ghcr.io/swadhinbiswas/opencodehub:latest + plan: free + region: singapore + healthCheckPath: /api/health + envVars: + # ── Server ── + - key: NODE_ENV + value: production + - key: PORT + value: "4321" + - key: HOST + value: "0.0.0.0" + - key: SITE_URL + # TODO: Update after first deploy with your actual Render URL + # Example: https://opencodehub.onrender.com + sync: false + + # ── Database (Render PostgreSQL) ── + - key: DATABASE_DRIVER + value: postgres + - key: DATABASE_URL + fromDatabase: + name: opencodehub-db + property: connectionString + - key: DATABASE_SSL + value: "true" + + # ── Redis (Upstash - Free) ── + # TODO: Create free Redis at https://upstash.com (region: Singapore) + # Then paste the Redis URL here: + - key: REDIS_URL + sync: false + + # ── Secrets (Auto-generated) ── + - key: JWT_SECRET + generateValue: true + - key: SESSION_SECRET + generateValue: true + - key: INTERNAL_HOOK_SECRET + generateValue: true + - key: CRON_SECRET + generateValue: true + - key: RUNNER_SECRET + generateValue: true + - key: AI_CONFIG_ENCRYPTION_KEY + generateValue: true + - key: WORKFLOW_SECRET_ENCRYPTION_KEY + generateValue: true + + # ── Storage ── + - key: STORAGE_TYPE + value: local + - key: GIT_REPOS_PATH + value: /data/repositories + - key: STORAGE_PATH + value: /data/storage + + # ── Features ── + - key: ENABLE_REGISTRATION + value: "true" + - key: ENABLE_WIKI + value: "true" + - key: ENABLE_ISSUES + value: "true" + - key: ENABLE_ACTIONS + value: "true" + - key: ENABLE_AI_ASSISTANT + value: "true" + + # ── Rate Limiting ── + - key: RATE_LIMIT_ENABLED + value: "true" + + # ── Logging ── + - key: LOG_LEVEL + value: info + + # Persistent disk for git repos, storage, SSH keys + disk: + name: opencodehub-data + mountPath: /data + sizeGB: 1 + +# ── PostgreSQL Database (Render Free) ──────────────────────── +databases: + - name: opencodehub-db + plan: free + databaseName: opencodehub + user: opencodehub + region: singapore diff --git a/scripts/ai-review.ts b/scripts/ai-review.ts index 8f35b2ae..1bc851be 100644 --- a/scripts/ai-review.ts +++ b/scripts/ai-review.ts @@ -1,14 +1,20 @@ import { execSync } from 'child_process'; +import { Mistral } from '@mistralai/mistralai'; +import { GoogleGenAI } from '@google/genai'; +import dotenv from 'dotenv'; -const GEMINI_API_KEY = process.env.GEMINI_API_KEY; +dotenv.config(); + +const GEMINI_API_KEY = process.env.GEMINI_KEY || process.env.GEMINI_API_KEY; +const AGENT_API_KEY = process.env.AGENT_API_KEY; const GITHUB_TOKEN = process.env.GITHUB_TOKEN; const PR_NUMBER = process.env.PR_NUMBER; const REPO = process.env.REPO; const BASE_SHA = process.env.BASE_SHA; const HEAD_SHA = process.env.HEAD_SHA; -if (!GEMINI_API_KEY) { - console.log("GEMINI_API_KEY is not set. Skipping AI review."); +if (!GEMINI_API_KEY && !AGENT_API_KEY) { + console.log("No AI provider API key found (GEMINI_KEY or AGENT_API_KEY). Skipping AI review."); process.exit(0); } @@ -33,8 +39,6 @@ async function run() { return; } - // 2. Call Gemini API - console.log("Requesting review from Gemini 1.5 Flash..."); const prompt = `You are a senior software engineer conducting a code review on a pull request. Review the following git diff and provide constructive, human-like, and professional feedback. Focus on identifying logic errors, security issues, performance bottlenecks, and best practice violations. @@ -51,44 +55,93 @@ ${diff} \`\`\` `; - // We use the same API endpoint format from your curl command - const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GEMINI_API_KEY}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - contents: [ - { - parts: [ - { - text: prompt - } - ] - } - ] - }) - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`Gemini API Error: ${response.status} - ${errorText}`); - process.exit(1); + let reviewComment = ""; + + if (AGENT_API_KEY) { + console.log("Requesting review from Mistral Agent..."); + const client = new Mistral({ apiKey: AGENT_API_KEY }); + const messages = [{ "role": "user" as const, "content": prompt }]; + + const response = await client.beta.conversations.start({ + agentId: 'ag_019f4ae0eb2e764aaa3f1dec318fc748', + agentVersion: 0, + inputs: messages as any, + }); + + reviewComment = (response as any)?.choices?.[0]?.message?.content || (response as any)?.message?.content || ""; + + if (!reviewComment) { + console.log("Could not find standard content in response. Fallback to full response."); + process.exit(1); + } + reviewComment += "\n\n— *AI Code Review (Powered by Mistral Agent)*"; + + } else if (GEMINI_API_KEY) { + console.log("Requesting review from Gemini 3.5 Flash..."); + const ai = new GoogleGenAI({ apiKey: GEMINI_API_KEY }); + const tools: any[] = [{ type: 'google_search' }]; + + // Fixed config: Using camelCase and nested thinkingConfig + const generationConfig = { + maxOutputTokens: 65536, + thinkingConfig: { thinkingBudget: 8192 }, + }; + + const interaction = await ai.interactions.create({ + model: 'models/gemini-3.5-flash', + input: prompt, + tools: tools, + generation_config: generationConfig as any, // the sdk still expects the property to be named generation_config + } as any); + + const step = interaction.steps?.at(-1); + reviewComment = (step as any)?.text || (step as any)?.parts?.[0]?.text || ""; + + if (!reviewComment) { + console.error("No content received from Gemini."); + process.exit(1); + } + reviewComment += "\n\n— *AI Code Review (Powered by Gemini)*"; } - const data = await response.json(); - let reviewComment = data.candidates?.[0]?.content?.parts?.[0]?.text; + // 3. Post to GitHub (and handle deduplication) + console.log("Fetching existing reviews..."); + const reviewsResponse = await fetch(`https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/reviews`, { + headers: { + 'Authorization': `Bearer ${GITHUB_TOKEN}`, + 'Accept': 'application/vnd.github.v3+json', + 'X-GitHub-Api-Version': '2022-11-28' + } + }); - if (!reviewComment) { - console.error("No content received from Gemini."); - process.exit(1); + if (reviewsResponse.ok) { + const reviews = await reviewsResponse.json(); + // Look for a previous review made by our bot + const previousReview = reviews.find((r: any) => r.body && r.body.includes('AI Code Review (Powered by')); + + if (previousReview) { + console.log(`Updating existing review #${previousReview.id}...`); + const updateResponse = await fetch(`https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/reviews/${previousReview.id}`, { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${GITHUB_TOKEN}`, + 'Accept': 'application/vnd.github.v3+json', + 'X-GitHub-Api-Version': '2022-11-28' + }, + body: JSON.stringify({ body: reviewComment }) + }); + + if (!updateResponse.ok) { + const ghError = await updateResponse.text(); + console.error(`GitHub API Error on update: ${updateResponse.status} - ${ghError}`); + process.exit(1); + } + console.log("Review successfully updated!"); + return; + } } - - // Add a signature - reviewComment += "\n\n— *AI Code Review (Powered by Gemini)*"; - // 3. Post to GitHub - console.log("Posting review to GitHub PR..."); + console.log("Posting new review to GitHub PR..."); const ghResponse = await fetch(`https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/reviews`, { method: 'POST', headers: { @@ -104,7 +157,7 @@ ${diff} if (!ghResponse.ok) { const ghError = await ghResponse.text(); - console.error(`GitHub API Error: ${ghResponse.status} - ${ghError}`); + console.error(`GitHub API Error on post: ${ghResponse.status} - ${ghError}`); process.exit(1); } diff --git a/scripts/migrate.ts b/scripts/migrate.ts new file mode 100644 index 00000000..7093113e --- /dev/null +++ b/scripts/migrate.ts @@ -0,0 +1,71 @@ +/** + * Apply committed Drizzle migrations to the database. + * + * Used by the Docker entrypoint and production deployment to apply schema + * migrations deterministically. Requires only `drizzle-orm` (a runtime + * dependency) — not `drizzle-kit` — so it works in slim production images. + * + * Usage: bun scripts/migrate.ts + * Env: DATABASE_URL, DATABASE_DRIVER, DATABASE_SSL, DATABASE_SSL_REJECT_UNAUTHORIZED + */ + +import { createClient } from "@libsql/client"; +import { config } from "dotenv"; +import Database from "better-sqlite3"; +import pg from "pg"; + +config({ override: true }); + +async function run() { + const driver = process.env.DATABASE_DRIVER || "postgres"; + + if (driver === "postgres") { + const { drizzle } = await import("drizzle-orm/node-postgres"); + const { migrate } = await import("drizzle-orm/node-postgres/migrator"); + + const url = + process.env.DATABASE_URL || + "postgresql://opencodehub:opencodehub@localhost:5432/opencodehub"; + const ssl = + process.env.DATABASE_SSL === "true" + ? { rejectUnauthorized: process.env.DATABASE_SSL_REJECT_UNAUTHORIZED !== "false" } + : undefined; + + const client = new pg.Client({ connectionString: url, ssl }); + await client.connect(); + try { + const db = drizzle(client); + await migrate(db, { migrationsFolder: "./drizzle" }); + } finally { + await client.end(); + } + } else if (driver === "sqlite") { + const { drizzle } = await import("drizzle-orm/better-sqlite3"); + const { migrate } = await import("drizzle-orm/better-sqlite3/migrator"); + + const url = process.env.DATABASE_URL || "./data/opencodehub.db"; + const client = new Database(url); + try { + const db = drizzle(client); + await migrate(db, { migrationsFolder: "./drizzle" }); + } finally { + client.close(); + } + } else { + // libsql / turso + const { drizzle } = await import("drizzle-orm/libsql"); + const { migrate } = await import("drizzle-orm/libsql/migrator"); + + const url = process.env.DATABASE_URL || "libsql://localhost:8000"; + const client = createClient({ url }); + const db = drizzle(client); + await migrate(db, { migrationsFolder: "./drizzle" }); + } + + console.log("✅ Database migrations applied"); +} + +run().catch((err) => { + console.error("❌ Database migration failed:", err); + process.exit(1); +}); diff --git a/scripts/reset-database.cjs b/scripts/reset-database.cjs index 3533224b..b0c5cb66 100644 --- a/scripts/reset-database.cjs +++ b/scripts/reset-database.cjs @@ -30,6 +30,7 @@ async function resetDatabase() { const sql = postgres(connectionString, { max: 1 }); const db = drizzle(sql); + void db; try { // Get all tables diff --git a/scripts/security/audit.mjs b/scripts/security/audit.mjs index 2dfce7c8..f09e1380 100755 --- a/scripts/security/audit.mjs +++ b/scripts/security/audit.mjs @@ -15,7 +15,7 @@ * 1 — fail (disallowed high/critical vulnerability present) * 2 — script error */ -import { existsSync, readFileSync, statSync, copyFileSync, mkdtempSync, rmSync, copyFileSync as copyFileSyncEsm } from "node:fs"; +import { existsSync, readFileSync, statSync, copyFileSync, mkdtempSync, rmSync } from "node:fs"; import { resolve, join } from "node:path"; import { tmpdir } from "node:os"; import { spawnSync } from "node:child_process"; @@ -30,30 +30,10 @@ const log = (level, msg) => console.log(`[${new Date().toISOString()}] [${level} function regenerateLockfile() { log("info", "Regenerating package-lock.json from package.json (--package-lock-only)…"); - // Some npm versions crash with `Cannot read properties of null (reading 'matches')` - // when the existing node_modules is bun-hoisted (paths contain `+`) or when - // hoisted packages reference the yarn/bun `workspace:` protocol in nested - // `node_modules//package.json` (e.g. crossws in docs-site/node_modules). - // We try three strategies in order: - // 1. Plain `npm install --package-lock-only` in the project root. - // 2. Same with `--ignore-scripts --legacy-peer-deps` to skip workspaces. - // 3. Run `npm install --package-lock-only` from a clean temp directory - // containing only package.json, so npm never sees bun's hoisted - // `node_modules/.bun/*` paths or the docs-site nested packages. - // If all three fail AND no lockfile exists on disk, exit 2; otherwise fall - // back to auditing the existing lockfile (may be stale but better than 0). const root = ROOT; const attempts = [ { - label: "root (default)", - run: () => spawnSync("npm", ["install", "--package-lock-only", "--no-audit", "--no-fund", `--registry=${AUDIT_REGISTRY}`], { cwd: root, stdio: "inherit" }), - }, - { - label: "root (--ignore-scripts --legacy-peer-deps)", - run: () => spawnSync("npm", ["install", "--package-lock-only", "--no-audit", "--no-fund", "--ignore-scripts", "--legacy-peer-deps", `--registry=${AUDIT_REGISTRY}`], { cwd: root, stdio: "inherit" }), - }, - { - label: "clean temp directory (no hoisted node_modules)", + label: "clean temp directory (isolated package.json)", run: () => { const tmp = mkdtempSync(join(tmpdir(), "och-audit-")); copyFileSync(join(root, "package.json"), join(tmp, "package.json")); @@ -68,14 +48,20 @@ function regenerateLockfile() { } }, }, + { + label: "root (--ignore-scripts --legacy-peer-deps)", + run: () => spawnSync("npm", ["install", "--package-lock-only", "--no-audit", "--no-fund", "--ignore-scripts", "--legacy-peer-deps", `--registry=${AUDIT_REGISTRY}`], { cwd: root, stdio: "inherit" }), + }, + { + label: "root (default)", + run: () => spawnSync("npm", ["install", "--package-lock-only", "--no-audit", "--no-fund", `--registry=${AUDIT_REGISTRY}`], { cwd: root, stdio: "inherit" }), + }, ]; for (let i = 0; i < attempts.length; i++) { const a = attempts[i]; log("info", `Lockfile regen attempt ${i + 1}: ${a.label}`); const r = a.run(); if (r.status === 0) { - // The clean-tmp attempt copies the lockfile before returning, so a - // subsequent `existsSync(LOCKFILE)` check at the caller will pass. return; } log("warn", `Lockfile regen attempt ${i + 1} failed (exit ${r.status})`); @@ -117,8 +103,6 @@ function runAudit() { { encoding: "utf8" } ); - // npm may exit non-zero when vulnerabilities are found AND when the lockfile - // is malformed. We rely on the JSON report regardless of exit code. let report = {}; try { report = JSON.parse(r.stdout || "{}"); diff --git a/scripts/ssh-server.ts b/scripts/ssh-server.ts index b40824b0..7e19103a 100644 --- a/scripts/ssh-server.ts +++ b/scripts/ssh-server.ts @@ -7,15 +7,17 @@ import { join } from "path"; import { uploadRepoToStorage, getStorageRepoPath, isCloudStorage } from "@/lib/git-storage"; const DATA_DIR = join(process.cwd(), "data"); -const REPOS_DIR = join(DATA_DIR, "repos"); +const REPOS_DIR = process.env.GIT_REPOS_PATH || join(DATA_DIR, "repositories"); const SSH_DIR = join(DATA_DIR, "ssh"); -const HOST_KEY_PATH = join(SSH_DIR, "ssh_host_rsa_key"); +const HOST_KEY_PATH = process.env.GIT_SSH_HOST_KEY || join(SSH_DIR, "host_key"); +const SSH_PORT = parseInt(process.env.GIT_SSH_PORT || "2222", 10); async function main() { - console.log("Starting OpenCodeHub SSH Server..."); + console.log(`Starting OpenCodeHub SSH Server on port ${SSH_PORT}...`); + console.log(`Repository path: ${REPOS_DIR}`); await startSSHServer({ - port: 2222, + port: SSH_PORT, hostKeyPath: HOST_KEY_PATH, reposPath: REPOS_DIR, authenticateUser: async (_, key) => { @@ -23,7 +25,6 @@ async function main() { const db = getDatabase(); // Iterate over all keys to find a match - // In a production app, we would index by fingerprint or key type to optimize const allKeys = await db.query.sshKeys.findMany(); for (const dbKey of allKeys) { @@ -75,10 +76,16 @@ async function main() { if (!repo) return false; + // Check if user is admin + const user = await db.query.users.findFirst({ + where: eq(schema.users.id, userId) + }); + const isAdmin = user?.isAdmin === true; + if (operation === "read") { - return canReadRepo(userId, repo); + return canReadRepo(userId, repo, { isAdmin }); } else { - return canWriteRepo(userId, repo); + return canWriteRepo(userId, repo, { isAdmin }); } }, onPush: async (userId, repoPath, refs) => { @@ -104,11 +111,12 @@ async function main() { // 2. Trigger Workflows import("@/lib/workflows").then(({ triggerRepoWorkflows }) => { - refs.forEach(refLine => { - const [, newSha, refName] = refLine.split(" "); - // Only trigger on branch updates (not deletions or tags for now unless supported) - if (newSha !== "0000000000000000000000000000000000000000" && refName.startsWith("refs/heads/")) { - triggerRepoWorkflows(repo.id, newSha, refName, userId).catch(console.error); + refs.forEach(refName => { + // refs contains ref names like "refs/heads/main" + if (refName.startsWith("refs/heads/")) { + // We need to get the latest commit SHA for this ref + // For now, trigger with a placeholder - the workflow engine will resolve it + triggerRepoWorkflows(repo.id, "HEAD", refName, userId).catch(console.error); } }); }); diff --git a/scripts/test-storage-config.ts b/scripts/test-storage-config.ts index cba3a4ee..7215cc57 100644 --- a/scripts/test-storage-config.ts +++ b/scripts/test-storage-config.ts @@ -1,5 +1,5 @@ -import { getStorage, getStorageConfig, resetStorage } from "../src/lib/storage"; +import { getStorage, getStorageConfig } from "../src/lib/storage"; async function main() { console.log("--- Testing Storage Configuration ---"); diff --git a/scripts/worker.ts b/scripts/worker.ts index 4c9b4a8e..93b6888d 100644 --- a/scripts/worker.ts +++ b/scripts/worker.ts @@ -5,62 +5,123 @@ import { logger } from "@/lib/logger"; import { syncAllMirrors } from "@/lib/mirror-sync"; import { queueWorker } from "@/lib/queue-worker"; import { runDueDigests } from "@/lib/chat-notifications"; -import { and, eq, lt } from "drizzle-orm"; +import { eq } from "drizzle-orm"; import { createServer } from "http"; // ── Configuration ───────────────────────────────────────────────────────────── const WORKER_INTERVAL = parseInt(process.env.WORKER_INTERVAL || "5000", 10); -const MIRROR_SYNC_INTERVAL = parseInt( - process.env.MIRROR_SYNC_INTERVAL || "60000", - 10, -); -const CLEANUP_INTERVAL = parseInt( - process.env.CLEANUP_INTERVAL || "3600000", - 10, -); -const DIGEST_INTERVAL = parseInt( - process.env.DIGEST_INTERVAL || "300000", // Every 5 minutes - 10, -); +const MIRROR_SYNC_INTERVAL = parseInt(process.env.MIRROR_SYNC_INTERVAL || "60000", 10); +const CLEANUP_INTERVAL = parseInt(process.env.CLEANUP_INTERVAL || "3600000", 10); +const DIGEST_INTERVAL = parseInt(process.env.DIGEST_INTERVAL || "300000", 10); const HEALTH_PORT = parseInt(process.env.WORKER_HEALTH_PORT || "9090", 10); const MAX_RETRIES = parseInt(process.env.WORKER_MAX_RETRIES || "3", 10); -const STALE_JOB_TIMEOUT_MS = parseInt( - process.env.WORKER_STALE_TIMEOUT || "300000", - 10, -); +const STALE_JOB_TIMEOUT_MS = parseInt(process.env.WORKER_STALE_TIMEOUT || "300000", 10); + +// Circuit breaker settings +const CIRCUIT_BREAKER_THRESHOLD = parseInt(process.env.WORKER_CB_THRESHOLD || "10", 10); +const CIRCUIT_BREAKER_RESET_MS = parseInt(process.env.WORKER_CB_RESET_MS || "60000", 10); // ── State ───────────────────────────────────────────────────────────────────── let isShuttingDown = false; -let isHealthy = true; -let lastQueueRun = Date.now(); +let lastQueueRun = 0; let lastMirrorRun = 0; let lastCleanupRun = 0; let lastDigestRun = 0; -let consecutiveErrors = 0; -const MAX_CONSECUTIVE_ERRORS = 10; + +// Circuit breaker state per task +interface CircuitBreakerState { + failures: number; + lastFailureTime: number; + isOpen: boolean; + openedAt: number; +} +const circuitBreakers = new Map(); + +function getCircuitBreaker(name: string): CircuitBreakerState { + let cb = circuitBreakers.get(name); + if (!cb) { + cb = { failures: 0, lastFailureTime: 0, isOpen: false, openedAt: 0 }; + circuitBreakers.set(name, cb); + } + return cb; +} + +function isCircuitOpen(name: string): boolean { + const cb = getCircuitBreaker(name); + if (!cb.isOpen) return false; + if (Date.now() - cb.openedAt > CIRCUIT_BREAKER_RESET_MS) { + cb.isOpen = false; + cb.failures = 0; + logger.info({ task: name }, "Circuit breaker reset (half-open)"); + return false; + } + return true; +} + +function recordSuccess(name: string): void { + const cb = getCircuitBreaker(name); + cb.failures = 0; + cb.isOpen = false; +} + +function recordFailure(name: string): void { + const cb = getCircuitBreaker(name); + cb.failures++; + cb.lastFailureTime = Date.now(); + if (cb.failures >= CIRCUIT_BREAKER_THRESHOLD) { + cb.isOpen = true; + cb.openedAt = Date.now(); + logger.error({ task: name, failures: cb.failures }, "Circuit breaker OPENED"); + } +} + +// ── Exponential Backoff ──────────────────────────────────────────────────────── +async function backoff(attempt: number): Promise { + const delay = Math.min(1000 * Math.pow(2, attempt), 60000); + await sleep(delay + Math.random() * 1000); +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} // ── Queue Processor ─────────────────────────────────────────────────────────── async function runQueueProcessor() { + if (isCircuitOpen("queue-processor")) { + logger.warn("Queue processor circuit open — skipping"); + return; + } + const db = getDatabase(); try { - // 1. Reclaim stale jobs (stuck in "processing" too long) + // 1. Reclaim stale jobs (stuck in "running" too long) try { const staleThreshold = new Date(Date.now() - STALE_JOB_TIMEOUT_MS); const staleItems = await db.query.mergeQueueItems.findMany({ - where: eq(schema.mergeQueueItems.status, "processing"), - columns: { id: true, startedAt: true }, + where: eq(schema.mergeQueueItems.status, "running"), + columns: { id: true, startedAt: true, attemptCount: true }, }); for (const item of staleItems) { if (item.startedAt && new Date(item.startedAt) < staleThreshold) { - await (db as NodePgDatabase) - .update(schema.mergeQueueItems) - .set({ status: "queued" }) - .where(eq(schema.mergeQueueItems.id, item.id)); + const attempts = (item.attemptCount || 0) + 1; + if (attempts > MAX_RETRIES) { + await (db as NodePgDatabase) + .update(schema.mergeQueueItems) + .set({ status: "failed", completedAt: new Date() }) + .where(eq(schema.mergeQueueItems.id, item.id)); + logger.warn({ itemId: item.id, attempts }, "Queue item exceeded max retries — moved to dead letter"); + } else { + await (db as NodePgDatabase) + .update(schema.mergeQueueItems) + .set({ status: "queued" }) + .where(eq(schema.mergeQueueItems.id, item.id)); + logger.warn({ itemId: item.id, attempts }, "Reclaimed stale queue item with backoff"); + } } } - } catch { - // startedAt column may not exist yet — skip stale reclaim + } catch (err) { + logger.error({ err }, "Stale job reclaim failed (non-fatal)"); } // 2. Process pending items @@ -73,36 +134,26 @@ async function runQueueProcessor() { if (repoIds.length > 0) { logger.info({ repoCount: repoIds.length }, "Processing merge queues"); - // Process sequentially to avoid race conditions for (const repoId of repoIds) { if (isShuttingDown) break; try { await queueWorker.processQueue(repoId); } catch (error) { - logger.error( - { err: error, repoId }, - "Failed to process queue for repo", - ); + logger.error({ err: error, repoId }, "Failed to process queue for repo"); + recordFailure("queue-processor"); } } } - consecutiveErrors = 0; + recordSuccess("queue-processor"); lastQueueRun = Date.now(); } catch (error) { - consecutiveErrors++; - logger.error( - { err: error, consecutiveErrors }, - "Error in queue processor loop", - ); - if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { - isHealthy = false; - logger.error("Too many consecutive errors. Worker marked unhealthy."); - } + recordFailure("queue-processor"); + logger.error({ err: error }, "Fatal error in queue processor loop"); } } -// ── Controlled Loop (replaces setInterval) ──────────────────────────────────── +// ── Controlled Loop with Exponential Backoff ─────────────────────────────────── async function runLoop( name: string, fn: () => Promise, @@ -110,13 +161,25 @@ async function runLoop( getLastRun: () => number, setLastRun: (t: number) => void, ) { + let attempt = 0; while (!isShuttingDown) { const elapsed = Date.now() - getLastRun(); if (elapsed >= intervalMs) { + if (isCircuitOpen(name)) { + logger.warn({ task: name }, `Circuit open — skipping ${name}`); + await sleep(Math.min(intervalMs, 5000)); + continue; + } try { await fn(); + attempt = 0; } catch (error) { - logger.error({ err: error, task: name }, `${name} failed`); + attempt++; + logger.error({ err: error, task: name, attempt }, `${name} failed`); + recordFailure(name); + if (attempt < MAX_RETRIES) { + await backoff(attempt); + } } setLastRun(Date.now()); } @@ -124,31 +187,39 @@ async function runLoop( } } -function sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - // ── Health Check Server ─────────────────────────────────────────────────────── function startHealthServer() { const server = createServer((req, res) => { if (req.url === "/healthz" || req.url === "/health") { const timeSinceLastRun = Date.now() - lastQueueRun; - const healthy = isHealthy && timeSinceLastRun < WORKER_INTERVAL * 5; - res.writeHead(healthy ? 200 : 503, { - "Content-Type": "application/json", - }); - res.end( - JSON.stringify({ - status: healthy ? "healthy" : "unhealthy", - uptime: process.uptime(), - lastQueueRun: new Date(lastQueueRun).toISOString(), - consecutiveErrors, - shuttingDown: isShuttingDown, - }), - ); + const healthy = !isShuttingDown && timeSinceLastRun < WORKER_INTERVAL * 10; + res.writeHead(healthy ? 200 : 503, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ + status: healthy ? "healthy" : "unhealthy", + uptime: process.uptime(), + lastQueueRun: new Date(lastQueueRun).toISOString(), + circuitBreakers: Object.fromEntries( + Array.from(circuitBreakers.entries()).map(([k, v]) => [ + k, + { isOpen: v.isOpen, failures: v.failures }, + ]), + ), + shuttingDown: isShuttingDown, + })); } else if (req.url === "/ready") { - res.writeHead(isHealthy ? 200 : 503); - res.end(isHealthy ? "ready" : "not ready"); + const ready = !isShuttingDown; + res.writeHead(ready ? 200 : 503); + res.end(ready ? "ready" : "not ready"); + } else if (req.url === "/metrics") { + res.writeHead(200, { "Content-Type": "text/plain" }); + const lines: string[] = []; + lines.push("# HELP worker_circuit_breaker_state Circuit breaker open/closed per task"); + lines.push("# TYPE worker_circuit_breaker_state gauge"); + for (const [name, cb] of circuitBreakers) { + lines.push(`worker_circuit_breaker_state{task="${name}"} ${cb.isOpen ? 1 : 0}`); + lines.push(`worker_circuit_breaker_failures{task="${name}"} ${cb.failures}`); + } + res.end(lines.join("\n") + "\n"); } else { res.writeHead(404); res.end("Not Found"); @@ -168,9 +239,7 @@ function setupGracefulShutdown(healthServer: ReturnType) { isShuttingDown = true; logger.info({ signal }, "Worker shutting down gracefully..."); healthServer.close(); - - // Give in-flight jobs up to 30s to complete - await sleep(2000); + await sleep(10_000); logger.info("Worker shutdown complete."); process.exit(0); }; @@ -179,7 +248,6 @@ function setupGracefulShutdown(healthServer: ReturnType) { process.on("SIGTERM", () => shutdown("SIGTERM")); process.on("uncaughtException", (err) => { logger.error({ err }, "Uncaught exception in worker"); - isHealthy = false; }); process.on("unhandledRejection", (reason) => { logger.error({ reason }, "Unhandled rejection in worker"); @@ -195,6 +263,8 @@ async function startWorker() { cleanupInterval: CLEANUP_INTERVAL, healthPort: HEALTH_PORT, maxRetries: MAX_RETRIES, + circuitBreakerThreshold: CIRCUIT_BREAKER_THRESHOLD, + circuitBreakerResetMs: CIRCUIT_BREAKER_RESET_MS, }, "Starting OpenCodeHub Background Worker...", ); @@ -202,50 +272,16 @@ async function startWorker() { const healthServer = startHealthServer(); setupGracefulShutdown(healthServer); - // Run initial processing immediately await runQueueProcessor(); - // Start concurrent task loops await Promise.all([ - runLoop( - "queue-processor", - runQueueProcessor, - WORKER_INTERVAL, - () => lastQueueRun, - (t) => { - lastQueueRun = t; - }, - ), - runLoop( - "mirror-sync", - async () => { await syncAllMirrors(); }, - MIRROR_SYNC_INTERVAL, - () => lastMirrorRun, - (t) => { - lastMirrorRun = t; - }, - ), - runLoop( - "cleanup", - () => cleanupAllRepos(), - CLEANUP_INTERVAL, - () => lastCleanupRun, - (t) => { - lastCleanupRun = t; - }, - ), - runLoop( - "digest", - async () => { - logger.info("Running scheduled digests processing..."); - await runDueDigests(); - }, - DIGEST_INTERVAL, - () => lastDigestRun, - (t) => { - lastDigestRun = t; - }, - ), + runLoop("queue-processor", runQueueProcessor, WORKER_INTERVAL, () => lastQueueRun, (t) => { lastQueueRun = t; }), + runLoop("mirror-sync", async () => { await syncAllMirrors(); }, MIRROR_SYNC_INTERVAL, () => lastMirrorRun, (t) => { lastMirrorRun = t; }), + runLoop("cleanup", () => cleanupAllRepos(), CLEANUP_INTERVAL, () => lastCleanupRun, (t) => { lastCleanupRun = t; }), + runLoop("digest", async () => { + logger.info("Running scheduled digests processing..."); + await runDueDigests(); + }, DIGEST_INTERVAL, () => lastDigestRun, (t) => { lastDigestRun = t; }), ]); } diff --git a/smartcommit.sh b/smartcommit.sh new file mode 100644 index 00000000..3ccc67b8 --- /dev/null +++ b/smartcommit.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +set -euo pipefail + +GREEN="\033[0;32m" +BLUE="\033[0;34m" +YELLOW="\033[1;33m" +RED="\033[0;31m" +NC="\033[0m" + +git update-index --refresh >/dev/null 2>&1 || true + +FILES=$(git status --porcelain | awk '{print substr($0,4)}') + +if [[ -z "$FILES" ]]; then + echo -e "${GREEN}✓ Nothing to commit.${NC}" + exit 0 +fi + +commit_message() { + local file="$1" + local emoji="" + local message="" + + case "$file" in + *.md) + emoji="📝" + message="docs: update $(basename "$file" .md)" + ;; + *.css) + emoji="🎨" + message="style: improve $(basename "$file")" + ;; + *.astro) + emoji="✨" + message="feat: update $(basename "$file") page" + ;; + *.tsx|*.jsx) + emoji="✨" + message="feat: improve $(basename "$file")" + ;; + *.ts|*.js) + emoji="♻️" + message="refactor: update $(basename "$file")" + ;; + *.json|*.yaml|*.yml|*.toml) + emoji="🔧" + message="config: update $(basename "$file")" + ;; + *.sql) + emoji="🗄️" + message="db: update $(basename "$file")" + ;; + tests/*|*test*) + emoji="✅" + message="test: update $(basename "$file")" + ;; + src/db/*) + emoji="🗄️" + message="db: update $(basename "$file")" + ;; + src/pages/api/*) + emoji="🚀" + message="api: update $(basename "$file")" + ;; + src/components/*) + emoji="💄" + message="ui: improve $(basename "$file")" + ;; + docs/*) + emoji="📝" + message="docs: add $(basename "$file")" + ;; + *) + emoji="📦" + message="chore: update $(basename "$file")" + ;; + esac + + echo "$emoji $message" +} + +echo -e "${BLUE}Found $(echo "$FILES" | wc -l) changed files.${NC}" +echo + +while IFS= read -r file +do + [[ -z "$file" ]] && continue + + if [[ ! -e "$file" ]]; then + echo -e "${YELLOW}Skipping deleted file: $file${NC}" + continue + fi + + echo -e "${BLUE}→ $file${NC}" + + git add "$file" + + msg=$(commit_message "$file") + + if git diff --cached --quiet; then + echo -e "${YELLOW}No staged changes.${NC}" + continue + fi + + if git commit -m "$msg"; then + echo -e "${GREEN}✓ $msg${NC}" + else + echo -e "${RED}✗ Failed: $file${NC}" + git reset HEAD "$file" >/dev/null 2>&1 || true + fi + + echo + +done <<< "$FILES" + +echo -e "${GREEN}🎉 Finished committing all files.${NC}" diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx index e2694380..08e5882b 100644 --- a/src/components/ThemeToggle.tsx +++ b/src/components/ThemeToggle.tsx @@ -1,41 +1,47 @@ -import { Monitor, Moon, Sun } from "lucide-react"; +"use client"; + +import { Monitor, Moon, Sun, Palette } from "lucide-react"; import { useEffect, useState } from "react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; -type Theme = "light" | "dark" | "system"; +type Theme = "light" | "dark" | "system" | "github-dark" | "dracula" | "rose-pine" | "tokyo-night"; function getStoredTheme(): Theme { if (typeof window === "undefined") return "dark"; return (localStorage.getItem("theme") as Theme) || "system"; } -function resolveTheme(theme: Theme): "light" | "dark" { - if (theme === "system") { - return window.matchMedia("(prefers-color-scheme: dark)").matches - ? "dark" - : "light"; - } - return theme; -} - function applyTheme(theme: Theme) { - const resolved = resolveTheme(theme); const html = document.documentElement; - if (resolved === "dark") { - html.classList.add("dark"); - } else { + const isSystemDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + const activeTheme: string = theme === "system" ? (isSystemDark ? "dark" : "light") : theme; + + if (activeTheme === "light") { html.classList.remove("dark"); + html.removeAttribute("data-theme"); + } else { + html.classList.add("dark"); + if (activeTheme !== "dark" && activeTheme !== "system") { + html.setAttribute("data-theme", activeTheme); + } else { + html.removeAttribute("data-theme"); + } } } export function ThemeToggle() { - const [theme, setTheme] = useState("dark"); // SSR default + const [theme, setTheme] = useState("dark"); useEffect(() => { const stored = getStoredTheme(); setTheme(stored); applyTheme(stored); - // Listen for system changes const mq = window.matchMedia("(prefers-color-scheme: dark)"); const handler = () => { if (getStoredTheme() === "system") { @@ -46,29 +52,54 @@ export function ThemeToggle() { return () => mq.removeEventListener("change", handler); }, []); - const cycleTheme = () => { - const order: Theme[] = ["light", "dark", "system"]; - const idx = order.indexOf(theme); - const next = order[(idx + 1) % order.length]; - setTheme(next); - localStorage.setItem("theme", next); - applyTheme(next); + const changeTheme = (newTheme: Theme) => { + setTheme(newTheme); + localStorage.setItem("theme", newTheme); + applyTheme(newTheme); }; - const Icon = theme === "dark" ? Moon : theme === "light" ? Sun : Monitor; - const label = - theme === "dark" ? "Dark" : theme === "light" ? "Light" : "System"; - return ( - + + + + + + changeTheme("light")} className="cursor-pointer"> + + Light + + changeTheme("dark")} className="cursor-pointer"> + + Default Dark + + changeTheme("github-dark")} className="cursor-pointer"> + + GitHub Dark + + changeTheme("dracula")} className="cursor-pointer"> + + Dracula + + changeTheme("rose-pine")} className="cursor-pointer"> + + Rosé Pine + + changeTheme("tokyo-night")} className="cursor-pointer"> + + Tokyo Night + + changeTheme("system")} className="cursor-pointer border-t mt-1 pt-1"> + + System + + + ); } diff --git a/src/components/actions/PipelineVisualizer.tsx b/src/components/actions/PipelineVisualizer.tsx index f4c0b493..72179d40 100644 --- a/src/components/actions/PipelineVisualizer.tsx +++ b/src/components/actions/PipelineVisualizer.tsx @@ -126,7 +126,7 @@ export function PipelineVisualizer({ jobs, onJobClick, activeJobId }: PipelineVi }; const getNodeStyles = (status: string, conclusion: string | null, isActive: boolean) => { - let base = "bg-white/5 border-white/10 text-slate-300 hover:bg-white/10"; + let base = "bg-secondary border-border text-slate-300 hover:bg-secondary/80"; let shadow = ""; if (status === "queued") { @@ -152,7 +152,7 @@ export function PipelineVisualizer({ jobs, onJobClick, activeJobId }: PipelineVi return (
-
+
🌐

3D Globe unavailable

WebGL may not be supported

@@ -43,7 +43,7 @@ class GlobeErrorBoundary extends Component<{ children: ReactNode }, { hasError: const GlobeViz = lazy(() => import("./GlobeViz").catch(() => ({ default: () => (
-
+
🌐

Globe failed to load

@@ -88,7 +88,7 @@ export default function AdminDashboard() { -
Loading globe...
+
Loading globe...
}> @@ -139,7 +139,7 @@ export default function AdminDashboard() { {/* Right Column: Log Stream */}
- +
diff --git a/src/components/admin/AlertBanner.tsx b/src/components/admin/AlertBanner.tsx index e1636471..bf3bfe04 100644 --- a/src/components/admin/AlertBanner.tsx +++ b/src/components/admin/AlertBanner.tsx @@ -90,16 +90,16 @@ export default function AlertBanner({ alerts = defaultAlerts }: Props) { {/* Timestamp */} - + {alert.timestamp} {/* Dismiss button */}
diff --git a/src/components/admin/BottomPanel.tsx b/src/components/admin/BottomPanel.tsx index 5932806c..c7f8cd3f 100644 --- a/src/components/admin/BottomPanel.tsx +++ b/src/components/admin/BottomPanel.tsx @@ -100,7 +100,7 @@ export default function BottomPanel({ stats }: Props) {
-
+
Global repository statistics (Last 24h)
@@ -185,7 +185,7 @@ export default function BottomPanel({ stats }: Props) {
-
+
Active participants in code review
@@ -234,7 +234,7 @@ export default function BottomPanel({ stats }: Props) { {u?.avatar ? ( ) : ( - {String.fromCharCode(65 + i)} + {String.fromCharCode(65 + i)} )} ))} @@ -260,8 +260,8 @@ export default function BottomPanel({ stats }: Props) {
{(stats.languages || []).map((lang: any, i: number) => (
-
{lang.name}
-
+
{lang.name}
+
-
{lang.percentage}%
+
{lang.percentage}%
))}
diff --git a/src/components/admin/GlobeViz.tsx b/src/components/admin/GlobeViz.tsx index f597115d..320df473 100644 --- a/src/components/admin/GlobeViz.tsx +++ b/src/components/admin/GlobeViz.tsx @@ -21,7 +21,7 @@ class GlobeErrorBoundary extends Component<{ children: ReactNode }, { hasError: if (this.state.hasError) { return (
-
+
🌐

Globe unavailable

@@ -178,7 +178,7 @@ export default function GlobeViz({ activities = [] }: Props) { if (!mounted || webglSupported === null) { return (
-
Loading globe...
+
Loading globe...
); } diff --git a/src/components/admin/LiveLogStream.tsx b/src/components/admin/LiveLogStream.tsx index 2accf5f5..36a10104 100644 --- a/src/components/admin/LiveLogStream.tsx +++ b/src/components/admin/LiveLogStream.tsx @@ -23,7 +23,7 @@ const getTypeIcon = (type: string) => { case "fork": return ; case "star": return ; case "comment": return ; - default: return ; + default: return ; } }; @@ -34,7 +34,7 @@ const getTypeColor = (type: string) => { case "fork": return "text-purple-400"; case "star": return "text-yellow-400"; case "comment": return "text-cyan-400"; - default: return "text-gray-400"; + default: return "text-muted-foreground"; } }; @@ -74,10 +74,10 @@ export default function LiveLogStream({ logs }: Props) {

Data Stream

-
Real-Time
+
Real-Time
-
+
{currentTime}
@@ -96,7 +96,7 @@ export default function LiveLogStream({ logs }: Props) { className={`relative overflow-hidden rounded-lg bg-gradient-to-r ${getTypeBg(log.type)} p-2`} > {/* Timestamp */} -
+
{log.timestamp}
@@ -110,13 +110,13 @@ export default function LiveLogStream({ logs }: Props) { {/* Details */}
- #{log.id.substring(0, 7)} - : + #{log.id.substring(0, 7)} + : {log.type.toUpperCase().replace("_", " ")}
-
+
{log.message}
diff --git a/src/components/admin/QuickStatsRow.tsx b/src/components/admin/QuickStatsRow.tsx index 71e5c6d1..99685463 100644 --- a/src/components/admin/QuickStatsRow.tsx +++ b/src/components/admin/QuickStatsRow.tsx @@ -94,7 +94,7 @@ export default function QuickStatsRow({ stats }: Props) { {/* Glow effect */}
-
+
{/* Icon with gradient background */}
@@ -107,7 +107,7 @@ export default function QuickStatsRow({ stats }: Props) {
-
+
{widget.label}
diff --git a/src/components/admin/RecentActivity.tsx b/src/components/admin/RecentActivity.tsx index 952faaa8..b73974c1 100644 --- a/src/components/admin/RecentActivity.tsx +++ b/src/components/admin/RecentActivity.tsx @@ -29,9 +29,9 @@ export default function RecentActivity({ activities = [] }: Props) {
-

Recent Activity

+

Recent Activity

-
+
No recent activity
@@ -46,7 +46,7 @@ export default function RecentActivity({ activities = [] }: Props) { > -

Recent Activity

+

Recent Activity

@@ -68,12 +68,12 @@ export default function RecentActivity({ activities = [] }: Props) { {/* Content */}
-
- {activity.user} - {activity.action} +
+ {activity.user} + {activity.action} {activity.target}
-
+
{activity.time}
diff --git a/src/components/admin/SystemStatus.tsx b/src/components/admin/SystemStatus.tsx index dc5b6836..82966e63 100644 --- a/src/components/admin/SystemStatus.tsx +++ b/src/components/admin/SystemStatus.tsx @@ -73,14 +73,14 @@ function GlowingGauge({
{value}% - {label} + {label}
); @@ -120,14 +120,14 @@ export default function SystemStatus({ status }: SystemStatusProps) {
{/* Storage Linear Bar */}
-
+
Storage {status.storageUsage}%
-
+
- Uptime + Uptime
- {status.uptime} + {status.uptime}
@@ -152,7 +152,7 @@ export default function SystemStatus({ status }: SystemStatusProps) { {/* Runners */}
- Runners + Runners
; if (rank === 2) return ; if (rank === 3) return ; - return {rank}; + return {rank}; }; const getGradient = (index: number) => { @@ -56,7 +56,7 @@ export default function TrendingDevelopers({ developers }: Props) { {/* Hover glow effect */}
-
+
{/* Rank */}
{getRankIcon(dev.rank)} @@ -65,7 +65,7 @@ export default function TrendingDevelopers({ developers }: Props) { {/* Avatar with gradient border */}
- + {dev.name[0].toUpperCase()}
@@ -73,7 +73,7 @@ export default function TrendingDevelopers({ developers }: Props) { {/* Info */}
-
+
{dev.name}
diff --git a/src/components/dashboard/ActivityStream.tsx b/src/components/dashboard/ActivityStream.tsx index ba4b10e6..3a69497c 100644 --- a/src/components/dashboard/ActivityStream.tsx +++ b/src/components/dashboard/ActivityStream.tsx @@ -28,7 +28,7 @@ export function ActivityStream({ activeCount, users, className }: ActivityStream ]; return ( -
+
{/* Subtle background glow effect using CSS gradients */}
@@ -79,21 +79,21 @@ export function ActivityStream({ activeCount, users, className }: ActivityStream
{users.slice(0, 5).map((user, i) => (
- + {user.username.slice(0, 2).toUpperCase()} {/* Tooltip on hover */} -
+
@{user.username}
))} {users.length > 5 && (
-
+
+{users.length - 5}
diff --git a/src/components/dashboard/StackCard.astro b/src/components/dashboard/StackCard.astro index 4f3256e1..26bab0fd 100644 --- a/src/components/dashboard/StackCard.astro +++ b/src/components/dashboard/StackCard.astro @@ -27,23 +27,23 @@ const isFullyMerged = stack.prs.every(pr => pr.isMerged); const hasFailures = stack.prs.some(pr => pr.status === 'failure' || pr.status === 'error'); const isDraft = stack.prs.some(pr => pr.status === 'draft'); -const statusColor = isFullyMerged ? "text-green-400" : hasFailures ? "text-red-400" : isDraft ? "text-gray-400" : "text-blue-400"; +const statusColor = isFullyMerged ? "text-green-400" : hasFailures ? "text-red-400" : isDraft ? "text-muted-foreground" : "text-blue-400"; const statusBorder = isFullyMerged ? "border-green-500/30" : hasFailures ? "border-red-500/30" : isDraft ? "border-gray-500/30" : "border-blue-500/30"; const statusBg = isFullyMerged ? "bg-green-500/10" : hasFailures ? "bg-red-500/10" : isDraft ? "bg-gray-500/10" : "bg-blue-500/10"; ---
-
+
{stack.name} - in {stack.repoOwner}/{stack.repoName} + in {stack.repoOwner}/{stack.repoName}
-
+
Updated {new Date(stack.updatedAt).toLocaleDateString()}
@@ -67,15 +67,15 @@ const statusBg = isFullyMerged ? "bg-green-500/10" : hasFailures ? "bg-red-500/1 ? "bg-green-500 border-green-500" : pr.isClosed ? "bg-red-500 border-red-500" - : "bg-[#0d1117] border-blue-500" + : "bg-card border-blue-500" }`} />
- + {pr.title} -
+
#{pr.number} - + {user.username[0].toUpperCase()} diff --git a/src/components/diff/DiffView.tsx b/src/components/diff/DiffView.tsx index 3a47ce66..ce3d8592 100644 --- a/src/components/diff/DiffView.tsx +++ b/src/components/diff/DiffView.tsx @@ -271,9 +271,9 @@ export function DiffView({ {/* Diff lines */} {!collapsedFiles.has(fileIdx) && ( -
+
{file.isBinary ? ( -
+
Binary file not shown.
) : ( @@ -305,7 +305,7 @@ export function DiffView({ ? "bg-[#0a3519] text-green-600" : line.type === "del" ? "bg-[#3d0f18] text-red-600" - : "text-gray-600"; + : "text-muted-foreground"; const issueForLine = line.newLine && codeQualityIssues diff --git a/src/components/home/FeatureDemosSection.tsx b/src/components/home/FeatureDemosSection.tsx index 26c3eb45..9558bf09 100644 --- a/src/components/home/FeatureDemosSection.tsx +++ b/src/components/home/FeatureDemosSection.tsx @@ -394,12 +394,14 @@ export default function FeatureDemosSection() { {/* Description */}
- +

{currentDemo.title}

{currentDemo.description}

Learn more diff --git a/src/components/home/HeroSection.tsx b/src/components/home/HeroSection.tsx index c28bba3b..aa7c09d2 100644 --- a/src/components/home/HeroSection.tsx +++ b/src/components/home/HeroSection.tsx @@ -94,24 +94,24 @@ export default function HeroSection() { initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: 0.4 }} - className="flex items-center justify-center gap-8 mt-16" + className="flex flex-col sm:flex-row items-center justify-center gap-4 sm:gap-6 md:gap-8 mt-12 sm:mt-16" >
- 100% - Open Source + 100% + Open Source
-
+
- Graphite - Features + Graphite + Features
-
+
- AI - Powered Reviews + AI + Powered Reviews
diff --git a/src/components/ide/WebIDE.tsx b/src/components/ide/WebIDE.tsx index 8eb9b04a..b4eadde0 100644 --- a/src/components/ide/WebIDE.tsx +++ b/src/components/ide/WebIDE.tsx @@ -199,7 +199,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile,
node.type === "file" ? handleOpenFile(node.path) : toggleDir(node, e)} @@ -250,18 +250,18 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile,
{/* VS Code Title Bar */}
-
+
logo - File - Edit - Selection - View - Go - Run - Terminal - Help + File + Edit + Selection + View + Go + Run + Terminal + Help
-
+
{activeFileData ? `${activeFileData.path.split("/").pop()} - ` : ""}{repo} - OpenCodeHub
@@ -279,7 +279,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile, if (activeTab === "explorer" && isSidebarOpen) setIsSidebarOpen(false); else { setActiveTab("explorer"); setIsSidebarOpen(true); } }} - className={cn("relative p-2 rounded-md hover:text-white transition-colors", activeTab === "explorer" && isSidebarOpen ? "text-white" : "text-[#858585]")} + className={cn("relative p-2 rounded-md hover:text-foreground transition-colors", activeTab === "explorer" && isSidebarOpen ? "text-foreground" : "text-[#858585]")} title="Explorer" > {activeTab === "explorer" && isSidebarOpen &&
} @@ -290,7 +290,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile, if (activeTab === "search" && isSidebarOpen) setIsSidebarOpen(false); else { setActiveTab("search"); setIsSidebarOpen(true); } }} - className={cn("relative p-2 rounded-md hover:text-white transition-colors", activeTab === "search" && isSidebarOpen ? "text-white" : "text-[#858585]")} + className={cn("relative p-2 rounded-md hover:text-foreground transition-colors", activeTab === "search" && isSidebarOpen ? "text-foreground" : "text-[#858585]")} title="Search" > {activeTab === "search" && isSidebarOpen &&
} @@ -301,23 +301,23 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile, if (activeTab === "scm" && isSidebarOpen) setIsSidebarOpen(false); else { setActiveTab("scm"); setIsSidebarOpen(true); } }} - className={cn("relative p-2 rounded-md hover:text-white transition-colors", activeTab === "scm" && isSidebarOpen ? "text-white" : "text-[#858585]")} + className={cn("relative p-2 rounded-md hover:text-foreground transition-colors", activeTab === "scm" && isSidebarOpen ? "text-foreground" : "text-[#858585]")} title="Source Control" > {activeTab === "scm" && isSidebarOpen &&
} {modifiedFiles.length > 0 && ( - + {modifiedFiles.length} )}
- + -
@@ -343,7 +343,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile,
{loading ? ( -
+
Loading...
) : ( @@ -354,7 +354,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile, )} {activeTab === "search" && ( -
+
Search is not available in the web editor.
)} @@ -372,7 +372,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile,
- {modifiedFiles.length} + {modifiedFiles.length}
{modifiedFiles.map(f => ( @@ -426,7 +426,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile, {filename} -
+
{f.isModified ? (
) : ( @@ -447,7 +447,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile,
{activeFileData.path.split("/").map((part, i, arr) => ( - {part} + {part} {i < arr.length - 1 && } ))} @@ -499,7 +499,7 @@ export function WebIDE({ owner, repo, defaultBranch, initialBranch, initialFile,
{/* Status Bar */} -
+
diff --git a/src/components/inbox/KeyboardReviewInbox.tsx b/src/components/inbox/KeyboardReviewInbox.tsx index 5961cd6a..ffa155b8 100644 --- a/src/components/inbox/KeyboardReviewInbox.tsx +++ b/src/components/inbox/KeyboardReviewInbox.tsx @@ -35,7 +35,7 @@ export const KeyboardReviewInbox: React.FC<{ initialPr: PRProps }> = ({ initialP }, [handleKeyDown]); return ( -
+

Reviewing: {initialPr.title}

diff --git a/src/components/issues/IssueDetail.tsx b/src/components/issues/IssueDetail.tsx index cdd14f00..2045bcfe 100644 --- a/src/components/issues/IssueDetail.tsx +++ b/src/components/issues/IssueDetail.tsx @@ -107,6 +107,10 @@ export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canL const [crossRepoTarget, setCrossRepoTarget] = useState(""); const [crossRepoType, setCrossRepoType] = useState("relates"); const [crossRepoError, setCrossRepoError] = useState(null); + const [isEditing, setIsEditing] = useState(false); + const [editTitle, setEditTitle] = useState(issue.title); + const [editBody, setEditBody] = useState(issue.body); + const [saving, setSaving] = useState(false); async function convertToEpic() { if (!confirm("Are you sure you want to convert this issue to an Epic?")) return; @@ -210,20 +214,47 @@ export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canL } } + async function handleSave() { + setSaving(true); + try { + const res = await fetch(`/api/repos/${repoOwner}/${repoName}/issues/${issue.number}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title: editTitle, description: editBody }), + }); + if (!res.ok) throw new Error("Failed to update issue"); + setIsEditing(false); + window.location.reload(); + } catch (e) { + alert("Failed to save changes"); + } finally { + setSaving(false); + } + } + return (
{/* Title Section */}
-
-

- {issue.title} -

- +
+ {isEditing ? ( + setEditTitle(e.target.value)} + className="flex-1 text-2xl md:text-3xl font-bold text-foreground bg-background border border-border rounded-lg px-3 py-1 focus:outline-none focus:ring-2 focus:ring-ring" + /> + ) : ( +

+ {issue.title} +

+ )} + #{issue.number}
@@ -241,14 +272,33 @@ export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canL )} {/* Edit Button */} - - - Edit - + {isEditing ? ( +
+ + +
+ ) : ( + setIsEditing(true)} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + className="flex items-center gap-2 px-4 py-2 rounded-lg border border-border bg-secondary text-sm text-muted-foreground hover:bg-secondary/80 transition-all" + > + + Edit + + )}
@@ -258,7 +308,7 @@ export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canL {issue.type === 'epic' && } {issue.type === 'task' && } @@ -299,8 +349,8 @@ export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canL
- - + + {issue.author.username} {" "}opened this issue {timeAgo(issue.createdAt)} @@ -311,7 +361,19 @@ export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canL
{/* Left Column - Issue Body & Comments */}
-
+ {isEditing ? ( +
+ +
+ +
+ + +
+ +
+ + Cancel + + +
+ +
+ + + diff --git a/src/pages/[owner]/[repo]/packages/index.astro b/src/pages/[owner]/[repo]/packages/index.astro index 319ff436..f1f50585 100644 --- a/src/pages/[owner]/[repo]/packages/index.astro +++ b/src/pages/[owner]/[repo]/packages/index.astro @@ -80,14 +80,14 @@ function timeAgo(date: Date): string {
-

Packages

-

+

Packages

+

{total} package{total !== 1 ? "s" : ""} published

Publish Package @@ -101,8 +101,8 @@ function timeAgo(date: Date): string { href={pt.value ? `?type=${pt.value}` : `/${owner}/${repo}/packages`} class={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${ (typeFilter || "") === pt.value - ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm" - : "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white" + ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-foreground shadow-sm" + : "text-muted-foreground dark:text-muted-foreground hover:text-gray-900 dark:hover:text-foreground" }`} > {pt.icon} @@ -117,7 +117,7 @@ function timeAgo(date: Date): string { name="q" value={search || ""} placeholder="Search packages..." - class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder-gray-400" + class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-foreground placeholder-gray-400" />
@@ -126,15 +126,15 @@ function timeAgo(date: Date): string { {packagesWithVersions.length === 0 ? (
📦
-

No packages yet

-

+

No packages yet

+

Publish your first package using the npm or Docker CLI, or use the API directly.

-
# npm
+
# npm
npm publish --registry {process.env.APP_URL}/api/packages/npm
-
# Docker
+
# Docker
docker push {new URL(process.env.APP_URL || "http://localhost").host}/myimage:latest
@@ -165,13 +165,13 @@ function timeAgo(date: Date): string {
{pkg.description && ( -

+

{pkg.description}

)}
-
+
{pkg.latestVersion?.version || "—"}
@@ -180,7 +180,7 @@ function timeAgo(date: Date): string {
-
+
{pkg.type.toUpperCase()} · {pkg.downloadCount.toLocaleString()} downloads diff --git a/src/pages/[owner]/[repo]/projects/[id].astro b/src/pages/[owner]/[repo]/projects/[id].astro index 504de539..14f6c481 100644 --- a/src/pages/[owner]/[repo]/projects/[id].astro +++ b/src/pages/[owner]/[repo]/projects/[id].astro @@ -38,20 +38,20 @@ const columns = await db.query.projectColumns.findMany({ class="h-[calc(100vh-14rem)] bg-mainBackgroundColor overflow-hidden rounded-xl border border-gray-800" >

{project.name} #{project.number}

-

{project.description}

+

{project.description}

-

Projects

+

Projects

{ permission !== "read" && (
@@ -478,7 +478,7 @@ try { CI/CD Pipeline Execution -
+
@@ -1795,7 +1795,7 @@ try { } loadAutoMergeStatus(); loadPrChecks(); - loadCodeOwnerEnforcement(); + // loadCodeOwnerEnforcement handled by CodeOwnersEnforcement component loadMergeReadiness(); mergeBtn?.addEventListener("click", async () => { diff --git a/src/pages/[owner]/[repo]/pulls/index.astro b/src/pages/[owner]/[repo]/pulls/index.astro index e18a30ba..4c316b21 100644 --- a/src/pages/[owner]/[repo]/pulls/index.astro +++ b/src/pages/[owner]/[repo]/pulls/index.astro @@ -39,7 +39,7 @@ if (!hasAccess) { // Fetch issue count const issueCountResult = await db - .select({ count: sql`count` }) + .select({ count: sql`count(*)` }) .from(schema.issues) .where( and( diff --git a/src/pages/[owner]/[repo]/pulls/new.astro b/src/pages/[owner]/[repo]/pulls/new.astro index 0827d50f..f6275540 100644 --- a/src/pages/[owner]/[repo]/pulls/new.astro +++ b/src/pages/[owner]/[repo]/pulls/new.astro @@ -94,7 +94,7 @@ if (baseBranch !== headBranch) { // Fetch open issue count (for header) const issueCountResult = await db - .select({ count: sql`count` }) + .select({ count: sql`count(*)` }) .from(schema.issues) .where( and( diff --git a/src/pages/[owner]/[repo]/releases/index.astro b/src/pages/[owner]/[repo]/releases/index.astro index dd0fc2c1..2b31679c 100644 --- a/src/pages/[owner]/[repo]/releases/index.astro +++ b/src/pages/[owner]/[repo]/releases/index.astro @@ -101,7 +101,7 @@ const repo = { {canWrite && ( Draft a new release diff --git a/src/pages/[owner]/[repo]/releases/new.astro b/src/pages/[owner]/[repo]/releases/new.astro index 4d76e0f9..7c49de26 100644 --- a/src/pages/[owner]/[repo]/releases/new.astro +++ b/src/pages/[owner]/[repo]/releases/new.astro @@ -139,7 +139,7 @@ const repo = {
diff --git a/src/pages/[owner]/[repo]/settings/actions/index.astro b/src/pages/[owner]/[repo]/settings/actions/index.astro index f207e09e..7bc46928 100644 --- a/src/pages/[owner]/[repo]/settings/actions/index.astro +++ b/src/pages/[owner]/[repo]/settings/actions/index.astro @@ -65,7 +65,7 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
@@ -75,7 +75,7 @@ const [initialTemplates, existingWorkflows] = await Promise.all([ @@ -84,14 +84,14 @@ const [initialTemplates, existingWorkflows] = await Promise.all([ Webhooks -
+
@@ -117,7 +117,7 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
-

+

@@ -126,7 +126,7 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
Actions Settings

-

+

Manage workflow permissions, artifact settings, and action runners

@@ -134,8 +134,8 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
-
-

+
+

@@ -143,31 +143,31 @@ const [initialTemplates, existingWorkflows] = await Promise.all([

-
-
-

+
+

@@ -189,31 +189,31 @@ const [initialTemplates, existingWorkflows] = await Promise.all([

-
-
+
@@ -224,21 +224,21 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
-
-

+
+

Workflow templates

-

+

Browse official workflow templates and apply them directly to this repository.

- - @@ -247,8 +247,8 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
- - @@ -257,11 +257,11 @@ const [initialTemplates, existingWorkflows] = await Promise.all([
- +
-
+
{initialTemplates.length === 0 ? ( -
No templates found.
+
No templates found.
) : ( initialTemplates.slice(0, 6).map((template) => (
-
{template.name}
-
+
{template.name}
+
{(template.category || "uncategorized").toUpperCase()} {template.language ? ` • ${template.language}` : ""}
{template.description && ( -

{template.description}

+

{template.description}

)}
diff --git a/src/pages/[owner]/[repo]/settings/branches/index.astro b/src/pages/[owner]/[repo]/settings/branches/index.astro index 6dd90885..9cc1276a 100644 --- a/src/pages/[owner]/[repo]/settings/branches/index.astro +++ b/src/pages/[owner]/[repo]/settings/branches/index.astro @@ -51,13 +51,13 @@ const repoHeaderData = {

Branch Protection Rules

-

+

Define rules to enforce workflows on specific branches.

Add Rule @@ -66,7 +66,7 @@ const repoHeaderData = { { rules.length === 0 ? (
-

No branch protection rules defined yet.

+

No branch protection rules defined yet.

) : (
- - +
+ @@ -89,7 +89,7 @@ const repoHeaderData = { {rules.map((rule) => ( -
Branch Pattern Required Approvals
+ {rule.pattern} diff --git a/src/pages/[owner]/[repo]/settings/branches/new.astro b/src/pages/[owner]/[repo]/settings/branches/new.astro index 1e7b3877..61423f91 100644 --- a/src/pages/[owner]/[repo]/settings/branches/new.astro +++ b/src/pages/[owner]/[repo]/settings/branches/new.astro @@ -28,7 +28,7 @@ if (!repository || repository.owner.username !== owner) { class="space-y-6 rounded-lg border border-gray-700 bg-gray-800 p-6" >
- -

+

Glob patterns supported (e.g., release/*)

- @@ -56,10 +56,10 @@ if (!repository || repository.owner.username !== owner) { class="mt-1" />
- -

+

All commits must be made to a non-protected branch and submitted via a pull request.

@@ -67,7 +67,7 @@ if (!repository || repository.owner.username !== owner) {
- Required approvals
@@ -87,7 +87,7 @@ if (!repository || repository.owner.username !== owner) { class="mt-1" />
- @@ -104,7 +104,7 @@ if (!repository || repository.owner.username !== owner) {
@@ -120,10 +120,10 @@ if (!repository || repository.owner.username !== owner) { class="mt-1" />
- -

+

Permit force pushes for all users with write access.

@@ -133,13 +133,13 @@ if (!repository || repository.owner.username !== owner) {
Cancel diff --git a/src/pages/[owner]/[repo]/settings/cloud-infra.astro b/src/pages/[owner]/[repo]/settings/cloud-infra.astro index 361ddf66..46f144d3 100644 --- a/src/pages/[owner]/[repo]/settings/cloud-infra.astro +++ b/src/pages/[owner]/[repo]/settings/cloud-infra.astro @@ -196,7 +196,7 @@ const repo = { if (!target) return; target.innerHTML = deployments.length === 0 ? emptyLabel - : deployments.map((item) => `
${item.status}${(item.commitSha || "").slice(0, 10)}
`).join(""); + : deployments.map((item) => `
${item.status}${(item.commitSha || "").slice(0, 10)}
`).join(""); } async function fetchSummary() { diff --git a/src/pages/[owner]/[repo]/settings/deploy-keys.astro b/src/pages/[owner]/[repo]/settings/deploy-keys.astro index 07414344..da1d1a5e 100644 --- a/src/pages/[owner]/[repo]/settings/deploy-keys.astro +++ b/src/pages/[owner]/[repo]/settings/deploy-keys.astro @@ -70,7 +70,7 @@ try {
-

+

@@ -200,14 +200,14 @@ try {
Deploy Keys

-

+

Deploy keys grant read-only or read/write access to a single repository.

+ +
+

About Deploy Keys

+
+

Deploy keys are SSH keys that grant access to a single repository. They're used for:

+
    +
  • CI/CD pipelines to pull code during builds
  • +
  • Deployment servers to pull code for deployment
  • +
  • Automation scripts that need repository access
  • +
+
+

Key differences from SSH keys:

+
    +
  • Deploy keys are tied to a single repository
  • +
  • SSH keys (in Settings) give access to all your repositories
  • +
  • Deploy keys can be read-only or read/write
  • +
+
+
+

To add a deploy key:

+
ssh-keygen -t ed25519 -C "deploy@production-server"
+

Then copy the public key and paste it in the form above.

+
+
+
+