Co-auth
commit Co-auth
336 files changed
+33834 -9238
Showing 75 changed files+5196-3318
This diff is too large and has been truncated. Displaying first 75 files.
.astro/settings.json.env.example.github/workflows/ai-review.yml.github/workflows/ci.yml.github/workflows/docker-publish.yml.gitignore.mimocode/.cron-lock.mimocode/plans/1784309305424-sunny-pixel.mdAGENTS.mdDEPLOYMENT.mdDOCKERHUB.mdDockerfileDockerfile.workerPRODUCTION_READINESS.mdREADME.mdVercel Deploy.mdastro.config.mjsbun.lockcli/src/commands/stack/index.tsdeploy/prometheus.ymldoc/docs-parity-baseline.jsondocker-compose.nas.ymldocker-compose.production.ymldocker-compose.ymldocker-entrypoint.shdocs-site/README.mddocs-site/astro.config.mjsdocs-site/src/content/docs/FREE-DEPLOYMENT.mddocs-site/src/content/docs/RENDER-DEPLOYMENT.mddocs-site/src/content/docs/administration/air-gapped.mddocs-site/src/content/docs/administration/backup-restore.mddocs-site/src/content/docs/administration/configuration.mddocs-site/src/content/docs/administration/deploy-nas.mddocs-site/src/content/docs/administration/deployment.mddocs-site/src/content/docs/administration/expose-nas.mddocs-site/src/content/docs/administration/horizontal-scaling.mddocs-site/src/content/docs/administration/monitoring.mddocs-site/src/content/docs/development/architecture.mddocs-site/src/content/docs/development/contributing.mddocs-site/src/content/docs/development/database-schema.mddocs-site/src/content/docs/development/local-dev-setup.mddocs-site/src/content/docs/development/release-gates.mddocs-site/src/content/docs/features/ai-review.mddocs-site/src/content/docs/getting-started/first-repository.mddocs-site/src/content/docs/getting-started/installation.mddocs-site/src/content/docs/getting-started/quick-start.mddocs-site/src/content/docs/guides/example.mddocs-site/src/content/docs/index.mdxdocs-site/src/content/docs/reference/api-versioning.mddocs-site/src/content/docs/reference/cli-auth-config.mddocs-site/src/content/docs/reference/cli-core-commands.mddocs-site/src/content/docs/reference/example.mddocs-site/src/content/docs/tutorials/zero-to-production.mddocs-site/src/custom.cssdocs/FREE-DEPLOYMENT.mddocs/RENDER-DEPLOYMENT.mddocs/administration/air-gapped.mddocs/administration/backup-restore.mddocs/administration/configuration.mddocs/administration/deploy-nas.mddocs/administration/deployment.mddocs/administration/expose-nas.mddocs/administration/horizontal-scaling.mddocs/administration/incident-runbook.mddocs/administration/known-accepted-vulns.jsondocs/administration/monitoring.mddocs/cli.mddocs/development/contributing.mddocs/development/release-gates.mddocs/getting-started/installation.mddocs/getting-started/quick-start.mddocs/getting-started/ssh-keys.mddocs/reference/api-versioning.mddrizzle/0000_abandoned_king_bedlam.sqldrizzle/0000_black_monster_badoon.sql
▼.astro/settings.json
+1-1
| @@ -1,5 +1,5 @@ | ||
1 | 1 | { |
2 | 2 | "_variables": { |
3 | - "lastUpdateCheck": 1783539359887 | |
3 | + "lastUpdateCheck": 1783961044818 | |
4 | 4 | } |
5 | 5 | } |
6 | 6 | |
▼.env.example
+10-0
| @@ -76,6 +76,11 @@ SSH_PORT=2222 | ||
76 | 76 | STORAGE_TYPE=local |
77 | 77 | STORAGE_PATH=./data/storage |
78 | 78 | |
79 | +# NAS / dedicated-disk deployments (docker-compose.nas.yml): | |
80 | +# Point every data directory at your NAS mount or dedicated disk. | |
81 | +# OCH_DATA_DIR=/mnt/nas/opencodehub | |
82 | +# STORAGE_PATH=./data/storage # or /mnt/nas/opencodehub/storage with local driver | |
83 | + | |
79 | 84 | # S3 Configuration (required when STORAGE_TYPE=s3) |
80 | 85 | # Works with AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, |
81 | 86 | # Ceph RGW, Wasabi, Backblaze B2, etc. |
| @@ -159,6 +164,11 @@ BACKUP_DIR=./backups | ||
159 | 164 | AIR_GAPPED_MODE=false |
160 | 165 | |
161 | 166 | # ── Observability (optional) ──────────────────────────────── |
167 | +# Bearer token protecting GET /api/metrics (Prometheus). Set a value in | |
168 | +# production; when empty, the endpoint is public and should be restricted | |
169 | +# at the reverse proxy. | |
170 | +METRICS_TOKEN= | |
171 | + | |
162 | 172 | # Grafana Cloud OTLP |
163 | 173 | GRAFANA_OTLP_ENDPOINT= |
164 | 174 | GRAFANA_INSTANCE_ID= |
165 | 175 | |
▼.github/workflows/ai-review.yml
+4-1
| @@ -22,11 +22,14 @@ jobs: | ||
22 | 22 | with: |
23 | 23 | bun-version: latest |
24 | 24 | |
25 | + - name: Install dependencies | |
26 | + run: bun install --frozen-lockfile | |
27 | + | |
25 | 28 | - name: Run AI Review |
26 | 29 | env: |
27 | 30 | # Use user's PAT to count towards their GitHub contribution graph, fallback to standard bot token |
28 | 31 | GITHUB_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} |
29 | - GEMINI_API_KEY: ${{ secrets.GEMINIKEY }} | |
32 | + AGENT_API_KEY: ${{ secrets.AGENT_API_KEY }} | |
30 | 33 | PR_NUMBER: ${{ github.event.pull_request.number }} |
31 | 34 | REPO: ${{ github.repository }} |
32 | 35 | BASE_SHA: ${{ github.event.pull_request.base.sha }} |
33 | 36 | |
▼.github/workflows/ci.yml
+22-7
| @@ -255,8 +255,18 @@ jobs: | ||
255 | 255 | steps: |
256 | 256 | - uses: actions/checkout@v4 |
257 | 257 | |
258 | + - name: Set up Docker Buildx | |
259 | + uses: docker/setup-buildx-action@v3 | |
260 | + | |
258 | 261 | - name: Build Docker image |
259 | - run: docker build -t opencodehub:ci -f Dockerfile . | |
262 | + uses: docker/build-push-action@v5 | |
263 | + with: | |
264 | + context: . | |
265 | + load: true | |
266 | + tags: opencodehub:ci | |
267 | + file: Dockerfile | |
268 | + cache-from: type=gha | |
269 | + cache-to: type=gha,mode=max | |
260 | 270 | |
261 | 271 | - name: Run Trivy vulnerability scanner |
262 | 272 | uses: aquasecurity/trivy-action@master |
| @@ -347,9 +357,11 @@ jobs: | ||
347 | 357 | DATABASE_DRIVER: postgres |
348 | 358 | - name: Start server and run perf check |
349 | 359 | run: | |
350 | - bun run dev & | |
360 | + HOST=127.0.0.1 PORT=4321 node ./dist/server/entry.mjs & | |
351 | 361 | SERVER_PID=$! |
352 | - sleep 8 | |
362 | + sleep 5 | |
363 | + curl -s http://127.0.0.1:4321/api/health > /dev/null || true | |
364 | + curl -s http://127.0.0.1:4321/api/metrics > /dev/null || true | |
353 | 365 | bun run perf:baseline |
354 | 366 | kill $SERVER_PID 2>/dev/null || true |
355 | 367 | env: |
| @@ -360,6 +372,7 @@ jobs: | ||
360 | 372 | INTERNAL_HOOK_SECRET: "ci-perf-hook" |
361 | 373 | CSRF_SKIP_DEV: "true" |
362 | 374 | RATE_LIMIT_SKIP_DEV: "true" |
375 | + RATE_LIMIT_ENABLED: "false" | |
363 | 376 | SKIP_REDIS_CHECK: "1" |
364 | 377 | PERF_BASE_URL: http://127.0.0.1:4321 |
365 | 378 | PERF_PATHS: /api/health,/api/metrics |
| @@ -460,6 +473,9 @@ jobs: | ||
460 | 473 | runs-on: ubuntu-latest |
461 | 474 | needs: [build, e2e] |
462 | 475 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' |
476 | + permissions: | |
477 | + contents: read | |
478 | + id-token: write | |
463 | 479 | steps: |
464 | 480 | - uses: actions/checkout@v4 |
465 | 481 | |
| @@ -482,7 +498,6 @@ jobs: | ||
482 | 498 | done |
483 | 499 | echo "::error::bun install failed after 3 attempts" |
484 | 500 | exit 1 |
485 | - working-directory: cli | |
486 | 501 | |
487 | 502 | - name: Build CLI |
488 | 503 | run: bun run build |
| @@ -500,7 +515,7 @@ jobs: | ||
500 | 515 | quality-gate: |
501 | 516 | name: Quality Gate |
502 | 517 | runs-on: ubuntu-latest |
503 | - needs: [lint, docs-parity, security, test, e2e, container-scan, sast, perf, build] | |
518 | + needs: [lint, docs-parity, security, test, e2e, container-scan, sast, performance, build] | |
504 | 519 | if: always() |
505 | 520 | steps: |
506 | 521 | - name: Check all gates |
| @@ -515,13 +530,13 @@ jobs: | ||
515 | 530 | |
516 | 531 | echo "Docs Parity: ${{ needs.docs-parity.result }}" |
517 | 532 | echo "Security Gates: ${{ needs.security.result }}" |
518 | - echo "Perf Gate: ${{ needs.perf.result }}" | |
533 | + echo "Perf Gate: ${{ needs.performance.result }}" | |
519 | 534 | |
520 | 535 | if [[ "${{ needs.lint.result }}" != "success" ]] || \ |
521 | 536 | [[ "${{ needs.docs-parity.result }}" != "success" ]] || \ |
522 | 537 | [[ "${{ needs.security.result }}" != "success" ]] || \ |
523 | 538 | [[ "${{ needs.test.result }}" != "success" ]] || \ |
524 | - [[ "${{ needs.perf.result }}" != "success" ]] || \ | |
539 | + [[ "${{ needs.performance.result }}" != "success" ]] || \ | |
525 | 540 | [[ "${{ needs.build.result }}" != "success" ]]; then |
526 | 541 | echo "::error::Quality gate FAILED — required checks did not pass" |
527 | 542 | exit 1 |
528 | 543 | |
▼.github/workflows/docker-publish.yml
+66-20
| @@ -8,7 +8,8 @@ on: | ||
8 | 8 | |
9 | 9 | env: |
10 | 10 | REGISTRY: docker.io |
11 | - IMAGE_NAME: swadhinbiswas/opencodehub | |
11 | + IMAGE_NAME: opencodehub/opencodehub | |
12 | + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: "true" | |
12 | 13 | |
13 | 14 | jobs: |
14 | 15 | build-and-push: |
| @@ -21,6 +22,9 @@ jobs: | ||
21 | 22 | - name: Checkout |
22 | 23 | uses: actions/checkout@v4 |
23 | 24 | |
25 | + - name: Set up QEMU | |
26 | + uses: docker/setup-qemu-action@v3 | |
27 | + | |
24 | 28 | - name: Set up Docker Buildx |
25 | 29 | uses: docker/setup-buildx-action@v3 |
26 | 30 | |
| @@ -30,8 +34,8 @@ jobs: | ||
30 | 34 | username: ${{ secrets.DOCKER_USERNAME }} |
31 | 35 | password: ${{ secrets.DOCKER_TOKEN }} |
32 | 36 | |
33 | - - name: Extract metadata | |
34 | - id: meta | |
37 | + - name: Extract Metadata (App) | |
38 | + id: meta-app | |
35 | 39 | uses: docker/metadata-action@v5 |
36 | 40 | with: |
37 | 41 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} |
| @@ -42,41 +46,83 @@ jobs: | ||
42 | 46 | type=sha,prefix= |
43 | 47 | type=raw,value=latest,enable={{is_default_branch}} |
44 | 48 | labels: | |
45 | - org.opencontainers.image.title=OpenCodeHub | |
46 | - org.opencontainers.image.description=A modern, self-hosted Git platform for teams that want speed, control, and clean workflows. | |
49 | + org.opencontainers.image.title=OpenCodeHub Platform | |
50 | + org.opencontainers.image.description=A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review. | |
51 | + org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub | |
52 | + org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub | |
53 | + org.opencontainers.image.licenses=MIT | |
54 | + | |
55 | + - name: Extract Metadata (Worker) | |
56 | + id: meta-worker | |
57 | + uses: docker/metadata-action@v5 | |
58 | + with: | |
59 | + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-worker | |
60 | + tags: | | |
61 | + type=ref,event=branch | |
62 | + type=semver,pattern={{version}} | |
63 | + type=semver,pattern={{major}}.{{minor}} | |
64 | + type=sha,prefix= | |
65 | + type=raw,value=latest,enable={{is_default_branch}} | |
66 | + labels: | | |
67 | + org.opencontainers.image.title=OpenCodeHub Worker | |
68 | + org.opencontainers.image.description=Background task worker for OpenCodeHub (queues, webhooks, notifications). | |
47 | 69 | org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub |
48 | 70 | org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub |
49 | 71 | org.opencontainers.image.licenses=MIT |
50 | 72 | |
51 | - - name: Build and push app image | |
73 | + - name: Extract Metadata (Runner) | |
74 | + id: meta-runner | |
75 | + uses: docker/metadata-action@v5 | |
76 | + with: | |
77 | + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner | |
78 | + tags: | | |
79 | + type=ref,event=branch | |
80 | + type=semver,pattern={{version}} | |
81 | + type=semver,pattern={{major}}.{{minor}} | |
82 | + type=sha,prefix= | |
83 | + type=raw,value=latest,enable={{is_default_branch}} | |
84 | + labels: | | |
85 | + org.opencontainers.image.title=OpenCodeHub Runner | |
86 | + org.opencontainers.image.description=Docker-in-Docker CI/CD runner for OpenCodeHub pipelines. | |
87 | + org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub | |
88 | + org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub | |
89 | + org.opencontainers.image.licenses=MIT | |
90 | + | |
91 | + - name: Build and Push App Image | |
52 | 92 | uses: docker/build-push-action@v5 |
53 | 93 | with: |
54 | 94 | context: . |
55 | 95 | file: ./Dockerfile |
56 | 96 | push: true |
57 | - tags: ${{ steps.meta.outputs.tags }} | |
58 | - labels: ${{ steps.meta.outputs.labels }} | |
97 | + tags: ${{ steps.meta-app.outputs.tags }} | |
98 | + labels: ${{ steps.meta-app.outputs.labels }} | |
59 | 99 | cache-from: type=gha |
60 | 100 | cache-to: type=gha,mode=max |
61 | 101 | platforms: linux/amd64,linux/arm64 |
62 | 102 | |
63 | - - name: Build and push runner image | |
103 | + - name: Build and Push Worker Image | |
104 | + uses: docker/build-push-action@v5 | |
105 | + with: | |
106 | + context: . | |
107 | + file: ./Dockerfile.worker | |
108 | + push: true | |
109 | + tags: ${{ steps.meta-worker.outputs.tags }} | |
110 | + labels: ${{ steps.meta-worker.outputs.labels }} | |
111 | + cache-from: type=gha | |
112 | + cache-to: type=gha,mode=max | |
113 | + platforms: linux/amd64,linux/arm64 | |
114 | + | |
115 | + - name: Build and Push Runner Image | |
64 | 116 | uses: docker/build-push-action@v5 |
65 | 117 | with: |
66 | 118 | context: . |
67 | 119 | file: ./Dockerfile.runner |
68 | 120 | push: true |
69 | - tags: | | |
70 | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner:latest | |
71 | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-runner:${{ github.sha }} | |
72 | - labels: | | |
73 | - org.opencontainers.image.title=OpenCodeHub Runner | |
74 | - org.opencontainers.image.description=CI/CD runner for OpenCodeHub | |
75 | - org.opencontainers.image.url=https://github.com/swadhinbiswas/OpencodeHub | |
76 | - org.opencontainers.image.source=https://github.com/swadhinbiswas/OpencodeHub | |
77 | - org.opencontainers.image.licenses=MIT | |
121 | + tags: ${{ steps.meta-runner.outputs.tags }} | |
122 | + labels: ${{ steps.meta-runner.outputs.labels }} | |
78 | 123 | cache-from: type=gha |
79 | 124 | cache-to: type=gha,mode=max |
125 | + platforms: linux/amd64,linux/arm64 | |
80 | 126 | |
81 | 127 | - name: Update Docker Hub Repository Description |
82 | 128 | uses: peter-evans/dockerhub-description@v4 |
| @@ -84,5 +130,5 @@ jobs: | ||
84 | 130 | username: ${{ secrets.DOCKER_USERNAME }} |
85 | 131 | password: ${{ secrets.DOCKER_TOKEN }} |
86 | 132 | repository: ${{ env.IMAGE_NAME }} |
87 | - readme-filepath: ./README.md | |
88 | - short-description: "A modern, self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI review." | |
133 | + readme-filepath: ./DOCKERHUB.md | |
134 | + short-description: "Self-hosted Git platform with stacked PRs, merge queue, CI/CD, and AI code review" | |
89 | 135 | |
▼.gitignore
+3-2
| @@ -61,8 +61,9 @@ pci/ | ||
61 | 61 | # Docker |
62 | 62 | .docker/ |
63 | 63 | |
64 | -# Drizzle | |
65 | -drizzle/meta/ | |
64 | +# Drizzle — migrations and the migration journal must be committed so that | |
65 | +# runtime containers can apply schema deterministically (scripts/migrate.ts) | |
66 | +# drizzle/ | |
66 | 67 | |
67 | 68 | # Monaco Editor cache |
68 | 69 | .monaco/ |
69 | 70 | |
▼.mimocode/.cron-lock
+1-0
| @@ -0,0 +1 @@ | ||
1 | +{"pid":8618,"startedAt":1784441536749} | |
0 | 2 | |
▼.mimocode/plans/1784309305424-sunny-pixel.md
+57-0
| @@ -0,0 +1,57 @@ | ||
1 | +# GitHub-like Profile Portfolio Page | |
2 | + | |
3 | +## Current State | |
4 | +The profile page at `src/pages/[owner]/index.astro` already has: | |
5 | +- Avatar, display name, username, bio | |
6 | +- Details (company, location, email, website, join date) | |
7 | +- Followers/following | |
8 | +- Contribution graph | |
9 | +- Repository list | |
10 | +- Activity feed | |
11 | + | |
12 | +## What Needs to Change | |
13 | + | |
14 | +On GitHub, when you visit `github.com/username`, you see: | |
15 | +1. **Left sidebar**: Avatar (large), name, username, bio, details, followers/following, "Edit profile" button | |
16 | +2. **Main content**: | |
17 | + - "Read me" section rendered from `username/username` repository's README.md | |
18 | + - "Pinned" repositories section (6 repos max, customizable) | |
19 | + - Contribution graph | |
20 | + - Activity feed | |
21 | + | |
22 | +The current OpenCodeHub page has this mostly but the layout and "Read me" section from the special repo are missing. | |
23 | + | |
24 | +## Implementation Plan | |
25 | + | |
26 | +### 1. Auto-create profile repository on user registration | |
27 | +- When a user registers, automatically create a `{username}/{username}` repository (like GitHub) | |
28 | +- Seed it with a default README.md containing the user's name and bio | |
29 | +- This repo should be private by default but its README is shown on the public profile | |
30 | + | |
31 | +### 2. Update profile page layout | |
32 | +- Move to GitHub's 2-column layout: left sidebar (avatar + details) + main content | |
33 | +- Left sidebar: Large avatar, name, username, bio, details, followers/following | |
34 | +- Main content: Render README.md from profile repo, pinned repos, contribution graph, activity | |
35 | + | |
36 | +### 3. Add "Read me" section | |
37 | +- Fetch `README.md` from `{username}/{username}` repository | |
38 | +- Render it as markdown in the profile page (like GitHub does) | |
39 | +- If no README exists, show the current default layout | |
40 | + | |
41 | +### 4. Add pinned repositories section | |
42 | +- Allow users to pin up to 6 repositories to their profile | |
43 | +- Store pinned repo IDs in user settings or a separate table | |
44 | +- Show pinned repos in a grid below the README | |
45 | + | |
46 | +### Files to Modify | |
47 | +- `src/pages/[owner]/index.astro` — Main profile page | |
48 | +- `src/pages/api/repos/index.ts` — Auto-create profile repo on registration | |
49 | +- `src/pages/register.astro` — Trigger profile repo creation | |
50 | +- `src/components/repo/ProfileREADME.tsx` — New component to render profile README | |
51 | +- `src/components/repo/PinnedRepos.tsx` — New component for pinned repos grid | |
52 | + | |
53 | +### Verification | |
54 | +1. Register a new user → profile repo auto-created with README | |
55 | +2. Visit `/{username}` → shows GitHub-like profile with README, pinned repos, contribution graph | |
56 | +3. Edit profile repo README → profile page updates | |
57 | +4. Pin/unpin repositories → grid updates | |
0 | 58 | |
▼AGENTS.md
+5-3
| @@ -294,7 +294,7 @@ Configured via `STORAGE_TYPE` env var (`local` or `s3`). | ||
294 | 294 | npm install -g opencodehub-cli |
295 | 295 | |
296 | 296 | # Auth |
297 | -och auth login --url http://localhost:3000 | |
297 | +och auth login --url http://localhost:4321 | |
298 | 298 | |
299 | 299 | # Stack workflow |
300 | 300 | och stack create feature/part-1 # Create stacked branch |
| @@ -343,7 +343,7 @@ npm run db:push | ||
343 | 343 | bun run scripts/seed-admin.ts # Create admin user |
344 | 344 | |
345 | 345 | # Dev |
346 | -npm run dev # Start dev server (port 3000) | |
346 | +npm run dev # Start dev server (port 4321) | |
347 | 347 | npm run git:start # Start SSH git server (port 2222) |
348 | 348 | npm run worker:start # Start background worker |
349 | 349 | npm run runner:start # Start CI runner |
| @@ -392,7 +392,9 @@ docker-compose up -d # Full stack with postgres + redis + runner | ||
392 | 392 | 4. Configure Redis for distributed locking |
393 | 393 | 5. Enable rate limiting (`RATE_LIMIT_ENABLED=true`) |
394 | 394 | 6. Set up email (SMTP) for notifications |
395 | -7. Configure storage backend (S3/GCS recommended for multi-node) | |
395 | +7. Configure storage backend (`local` for single-host/NAS, S3-compatible for multi-node) | |
396 | +8. Set `METRICS_TOKEN` to protect `GET /api/metrics` | |
397 | +9. Apply committed migrations (`bun run migrate` — runs automatically in containers) | |
396 | 398 | |
397 | 399 | ### Vercel/Edge |
398 | 400 | - Turso (LibSQL) recommended for edge deployment |
399 | 401 | |
▼DEPLOYMENT.md
+24-26
| @@ -87,31 +87,28 @@ DATABASE_DRIVER=postgres | ||
87 | 87 | DATABASE_URL=postgresql://user:password@host:5432/opencodehub |
88 | 88 | ``` |
89 | 89 | |
90 | -**Storage (Use cloud storage, not local):** | |
90 | +**Storage (local or S3-compatible — only two backends supported):** | |
91 | 91 | ```bash |
92 | +# local (default): filesystem, ideal for a dedicated disk or NAS mount | |
93 | +STORAGE_TYPE=local | |
94 | +STORAGE_PATH=/mnt/nas/opencodehub/storage # point at your NAS mount / dedicated disk | |
95 | + | |
96 | +# or S3-compatible (AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, Ceph RGW, ...) | |
92 | 97 | STORAGE_TYPE=s3 |
93 | 98 | STORAGE_BUCKET=opencodehub-production |
94 | 99 | STORAGE_REGION=us-east-1 |
95 | -S3_ACCESS_KEY=<your-access-key> | |
96 | -S3_ACCESS_KEY=<your-access-key> | |
97 | -S3_SECRET_KEY=<your-secret-key> | |
100 | +STORAGE_ENDPOINT= # leave empty for AWS S3; set for other vendors | |
101 | +STORAGE_ACCESS_KEY_ID=<your-access-key> | |
102 | +STORAGE_SECRET_ACCESS_KEY=<your-secret-key> | |
98 | 103 | ``` |
99 | 104 | |
100 | -**Google Drive + Turso Stack:** | |
101 | -See [docs/GDRIVE_STACK.md](docs/GDRIVE_STACK.md) for detailed setup. | |
102 | -```bash | |
103 | -STORAGE_TYPE=gdrive | |
104 | -GOOGLE_CLIENT_ID=... | |
105 | -GOOGLE_CLIENT_SECRET=... | |
106 | -GOOGLE_REFRESH_TOKEN=... | |
107 | -GOOGLE_FOLDER_ID=... | |
108 | -``` | |
105 | +> Only `local` and `s3` storage backends are supported. Google Drive, | |
106 | +> OneDrive, Dropbox, FTP, GCS, Azure, and rclone-as-adapter have been | |
107 | +> removed; use the `s3` driver with a vendor-specific `STORAGE_ENDPOINT`. | |
108 | +> See `docs/guides/storage-adapters.md`. | |
109 | 109 | |
110 | 110 | **Optional but Recommended:** |
111 | 111 | ```bash |
112 | -# Error Monitoring | |
113 | -SENTRY_DSN=https://your-sentry-dsn | |
114 | - | |
115 | 112 | # Email |
116 | 113 | SMTP_HOST=smtp.example.com |
117 | 114 | SMTP_PORT=587 |
| @@ -120,7 +117,7 @@ SMTP_PASSWORD=<smtp-password> | ||
120 | 117 | |
121 | 118 | |
122 | 119 | # Redis (for session management) |
123 | -REDIS_URL=redis://localhost:6379 | |
120 | +REDIS_URL=redis://:your-password@localhost:6379 | |
124 | 121 | ``` |
125 | 122 | |
126 | 123 | ### Deployment Steps |
| @@ -169,7 +166,7 @@ services: | ||
169 | 166 | build: . |
170 | 167 | restart: always |
171 | 168 | ports: |
172 | - - "3000:3000" | |
169 | + - "4321:4321" | |
173 | 170 | env_file: .env.production |
174 | 171 | depends_on: |
175 | 172 | - postgres |
| @@ -179,7 +176,7 @@ services: | ||
179 | 176 | environment: |
180 | 177 | NODE_ENV: production |
181 | 178 | healthcheck: |
182 | - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] | |
179 | + test: ["CMD", "curl", "-f", "http://localhost:4321/api/health"] | |
183 | 180 | interval: 30s |
184 | 181 | timeout: 10s |
185 | 182 | retries: 3 |
| @@ -208,14 +205,15 @@ volumes: | ||
208 | 205 | ``` |
209 | 206 | |
210 | 207 | ```bash |
211 | -# Deploy with Docker | |
212 | -docker-compose -f docker-compose.prod.yml up -d | |
208 | +# Deploy with the repository's compose files (kept in sync with the codebase) | |
209 | +docker compose up -d # quickstart | |
210 | +docker compose -f docker-compose.production.yml --profile production up -d # Caddy TLS | |
213 | 211 | |
214 | 212 | # View logs |
215 | -docker-compose -f docker-compose.prod.yml logs -f app | |
213 | +docker compose logs -f app | |
216 | 214 | |
217 | 215 | # Create admin user in container |
218 | -docker-compose exec app bun run scripts/seed-admin.ts | |
216 | +docker compose exec app bun run scripts/seed-admin.ts | |
219 | 217 | ``` |
220 | 218 | |
221 | 219 | ### Reverse Proxy (Nginx) |
| @@ -237,7 +235,7 @@ server { | ||
237 | 235 | |
238 | 236 | # Proxy configuration |
239 | 237 | location / { |
240 | - proxy_pass http://localhost:3000; | |
238 | + proxy_pass http://localhost:4321; | |
241 | 239 | proxy_set_header Host $host; |
242 | 240 | proxy_set_header X-Real-IP $remote_addr; |
243 | 241 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| @@ -251,7 +249,7 @@ server { | ||
251 | 249 | |
252 | 250 | # Git operations (larger timeouts) |
253 | 251 | location ~ ^/[^/]+/[^/]+\.git/ { |
254 | - proxy_pass http://localhost:3000; | |
252 | + proxy_pass http://localhost:4321; | |
255 | 253 | proxy_read_timeout 600s; |
256 | 254 | proxy_send_timeout 600s; |
257 | 255 | proxy_buffer_size 128k; |
| @@ -481,7 +479,7 @@ curl -X POST https://git.yourcompany.com/api/admin/test-email \ | ||
481 | 479 | - **API Documentation:** https://git.yourcompany.com/api/docs |
482 | 480 | - **Admin Panel:** https://git.yourcompany.com/admin |
483 | 481 | - **User Guide:** See README.md |
484 | -- **Security Best Practices:** See production_analysis.md | |
482 | +- **Security Best Practices:** See docs/administration/security.md | |
485 | 483 | |
486 | 484 | --- |
487 | 485 | |
488 | 486 | |
▼DOCKERHUB.md
+209-0
| @@ -0,0 +1,209 @@ | ||
1 | +# OpenCodeHub | |
2 | + | |
3 | +[](https://hub.docker.com/r/opencodehub/opencodehub) | |
4 | +[](https://hub.docker.com/r/opencodehub/opencodehub) | |
5 | +[](https://opensource.org/licenses/MIT) | |
6 | +[](https://docs.opencodehub.space) | |
7 | + | |
8 | +**The self-hosted Git platform built for speed, control, and modern software engineering workflows.** | |
9 | + | |
10 | +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**. | |
11 | + | |
12 | +--- | |
13 | + | |
14 | +## Key Features | |
15 | + | |
16 | +- 🥞 **Stacked PRs** — Native stacked branch workflows in Web UI & CLI (`och stack create/submit/sync`) | |
17 | +- 🚦 **Merge Queue** — Priority lanes, speculatively tested builds, and auto-merge controls | |
18 | +- ⚡ **CI/CD Pipeline Engine** — GitHub Actions workflow runner using Docker-in-Docker execution | |
19 | +- 🤖 **AI Code Review** — Multi-provider support (OpenAI, Anthropic, Gemini, Groq, local Ollama) | |
20 | +- 🔒 **Enterprise Security** — Secret scanning, Trivy vulnerability scans, license compliance, OIDC SSO, 2FA/TOTP, and path permissions | |
21 | +- 📦 **Pluggable Storage** — Local filesystem, AWS S3, Cloudflare R2, MinIO, Ceph, Garage, and any S3-compatible backend | |
22 | +- 🗄️ **Multi-Database Support** — PostgreSQL (recommended for production), SQLite, and Turso/LibSQL | |
23 | +- 🧰 **Developer CLI** — `opencodehub-cli` (`och`) with interactive terminal cockpit (`och focus`) and stack visualization | |
24 | + | |
25 | +--- | |
26 | + | |
27 | +## Architecture Overview | |
28 | + | |
29 | +``` | |
30 | +┌─────────────────────────────────────────────────────────────┐ | |
31 | +│ CLIENTS │ | |
32 | +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ | |
33 | +└─────────────────────────────────────────────────────────────┘ | |
34 | + │ | |
35 | +┌─────────────────────────────────────────────────────────────┐ | |
36 | +│ OPENCODEHUB PLATFORM │ | |
37 | +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ | |
38 | +│ │ Web UI │ │ REST API │ │ GraphQL Endpoint │ │ | |
39 | +│ │ (Astro+React)││ (140+ routes)││ (src/pages/api/) │ │ | |
40 | +│ └─────────────┘ └─ ────────────┘ └─────────────────────┘ │ | |
41 | +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ | |
42 | +│ │ Git Server │ │ SSH Server │ │ Pipeline Runner │ │ | |
43 | +│ │ (HTTP RPC) │ │ (ssh2) │ │ (Docker executor) │ │ | |
44 | +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ | |
45 | +└─────────────────────────────────────────────────────────────┘ | |
46 | + │ | |
47 | +┌─────────────────────────────────────────────────────────────┐ | |
48 | +│ PERSISTENCE & INFRASTRUCTURE │ | |
49 | +│ PostgreSQL / SQLite │ Redis │ S3 / MinIO / Local │ | |
50 | +└─────────────────────────────────────────────────────────────┘ | |
51 | +``` | |
52 | + | |
53 | +--- | |
54 | + | |
55 | +## Quick Start | |
56 | + | |
57 | +### 1. Single Container (Quick Test) | |
58 | + | |
59 | +```bash | |
60 | +docker run -d \ | |
61 | + --name opencodehub \ | |
62 | + -p 4321:4321 \ | |
63 | + -p 2222:2222 \ | |
64 | + -v opencodehub-data:/data \ | |
65 | + -e JWT_SECRET=$(openssl rand -hex 32) \ | |
66 | + -e SESSION_SECRET=$(openssl rand -hex 32) \ | |
67 | + -e INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) \ | |
68 | + -e SITE_URL=http://localhost:4321 \ | |
69 | + opencodehub/opencodehub:latest | |
70 | +``` | |
71 | + | |
72 | +Open **http://localhost:4321** in your browser to get started. | |
73 | + | |
74 | +--- | |
75 | + | |
76 | +### 2. Production Docker Compose Stack (Recommended) | |
77 | + | |
78 | +```yaml | |
79 | +version: "3.8" | |
80 | + | |
81 | +services: | |
82 | + app: | |
83 | + image: opencodehub/opencodehub:latest | |
84 | + container_name: opencodehub-app | |
85 | + restart: always | |
86 | + ports: | |
87 | + - "4321:4321" # Web UI & API | |
88 | + - "2222:2222" # Git SSH Server | |
89 | + environment: | |
90 | + - NODE_ENV=production | |
91 | + - SITE_URL=https://git.yourdomain.com | |
92 | + - DATABASE_DRIVER=postgres | |
93 | + - DATABASE_URL=postgresql://opencodehub:secretpass@postgres:5432/opencodehub?sslmode=disable | |
94 | + - REDIS_URL=redis://redis:6379 | |
95 | + - JWT_SECRET=${JWT_SECRET} | |
96 | + - SESSION_SECRET=${SESSION_SECRET} | |
97 | + - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET} | |
98 | + - RUNNER_SECRET=${RUNNER_SECRET} | |
99 | + - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY} | |
100 | + volumes: | |
101 | + - app-data:/data | |
102 | + depends_on: | |
103 | + - postgres | |
104 | + - redis | |
105 | + | |
106 | + worker: | |
107 | + image: opencodehub/opencodehub-worker:latest | |
108 | + container_name: opencodehub-worker | |
109 | + restart: always | |
110 | + environment: | |
111 | + - NODE_ENV=production | |
112 | + - DATABASE_DRIVER=postgres | |
113 | + - DATABASE_URL=postgresql://opencodehub:secretpass@postgres:5432/opencodehub?sslmode=disable | |
114 | + - REDIS_URL=redis://redis:6379 | |
115 | + volumes: | |
116 | + - app-data:/data | |
117 | + depends_on: | |
118 | + - postgres | |
119 | + - redis | |
120 | + | |
121 | + runner: | |
122 | + image: opencodehub/opencodehub-runner:latest | |
123 | + container_name: opencodehub-runner | |
124 | + restart: always | |
125 | + privileged: true | |
126 | + environment: | |
127 | + - OPENCODEHUB_URL=http://app:4321 | |
128 | + - RUNNER_SECRET=${RUNNER_SECRET} | |
129 | + volumes: | |
130 | + - /var/run/docker.sock:/var/run/docker.sock | |
131 | + depends_on: | |
132 | + - app | |
133 | + | |
134 | + postgres: | |
135 | + image: postgres:16-alpine | |
136 | + container_name: opencodehub-postgres | |
137 | + restart: always | |
138 | + environment: | |
139 | + POSTGRES_USER: opencodehub | |
140 | + POSTGRES_PASSWORD: secretpass | |
141 | + POSTGRES_DB: opencodehub | |
142 | + volumes: | |
143 | + - pg-data:/var/lib/postgresql/data | |
144 | + | |
145 | + redis: | |
146 | + image: redis:7-alpine | |
147 | + container_name: opencodehub-redis | |
148 | + restart: always | |
149 | + volumes: | |
150 | + - redis-data:/data | |
151 | + | |
152 | +volumes: | |
153 | + app-data: | |
154 | + pg-data: | |
155 | + redis-data: | |
156 | +``` | |
157 | + | |
158 | +Start the stack: | |
159 | +```bash | |
160 | +docker compose up -d | |
161 | +docker compose exec app bun run scripts/seed-admin.ts | |
162 | +``` | |
163 | + | |
164 | +--- | |
165 | + | |
166 | +## Environment Variables Reference | |
167 | + | |
168 | +| Variable | Required | Default | Description | | |
169 | +|----------|----------|---------|-------------| | |
170 | +| `SITE_URL` | Yes | `http://localhost:4321` | Base URL of your platform (e.g. `https://git.company.com`) | | |
171 | +| `DATABASE_DRIVER` | No | `postgres` | Database driver (`postgres`, `sqlite`, `turso`) | | |
172 | +| `DATABASE_URL` | Yes | — | Connection string (PostgreSQL/SQLite) | | |
173 | +| `REDIS_URL` | Yes | — | Redis connection URI for sessions & locks | | |
174 | +| `JWT_SECRET` | Yes | — | 32+ char secret for JWT token signing | | |
175 | +| `SESSION_SECRET` | Yes | — | 32+ char secret for cookie encryption | | |
176 | +| `INTERNAL_HOOK_SECRET` | Yes | — | Shared secret for Git hook callbacks | | |
177 | +| `STORAGE_TYPE` | No | `local` | Storage backend (`local` or `s3`) | | |
178 | +| `STORAGE_BUCKET` | If `STORAGE_TYPE=s3` | — | S3 bucket name | | |
179 | +| `STORAGE_REGION` | If `STORAGE_TYPE=s3` | `us-east-1` | S3 region | | |
180 | +| `STORAGE_ENDPOINT` | Optional | — | S3-compatible custom endpoint (MinIO / R2 / Garage) | | |
181 | +| `STORAGE_ACCESS_KEY_ID` | If `STORAGE_TYPE=s3` | — | Access Key ID | | |
182 | +| `STORAGE_SECRET_ACCESS_KEY` | If `STORAGE_TYPE=s3` | — | Secret Access Key | | |
183 | +| `METRICS_TOKEN` | Optional | — | Bearer token for `GET /api/metrics` | | |
184 | + | |
185 | +--- | |
186 | + | |
187 | +## OpenCodeHub CLI (`och`) | |
188 | + | |
189 | +Install the official CLI to manage stacks, merge queues, and reviews: | |
190 | + | |
191 | +```bash | |
192 | +npm install -g opencodehub-cli | |
193 | + | |
194 | +# Authenticate with your self-hosted instance | |
195 | +och auth login --url https://git.yourdomain.com | |
196 | + | |
197 | +# Create stacked PR branches | |
198 | +och stack create feature/part-1 | |
199 | +och stack submit | |
200 | +``` | |
201 | + | |
202 | +--- | |
203 | + | |
204 | +## Useful Links & Community | |
205 | + | |
206 | +- **Official Repository**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub) | |
207 | +- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space) | |
208 | +- **Bug Tracker**: [github.com/swadhinbiswas/OpencodeHub/issues](https://github.com/swadhinbiswas/OpencodeHub/issues) | |
209 | +- **License**: MIT | |
0 | 210 | |
▼Dockerfile
+29-11
| @@ -6,7 +6,7 @@ LABEL org.opencontainers.image.title="OpenCodeHub" \ | ||
6 | 6 | org.opencontainers.image.source="https://github.com/swadhinbiswas/OpencodeHub" \ |
7 | 7 | org.opencontainers.image.licenses="MIT" |
8 | 8 | |
9 | -# Install dependencies | |
9 | +# Install ALL dependencies (needed for build including CSS tooling) | |
10 | 10 | FROM base AS deps |
11 | 11 | RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* |
12 | 12 | COPY package.json bun.lock ./ |
| @@ -16,6 +16,19 @@ RUN for i in 1 2 3; do \ | ||
16 | 16 | (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \ |
17 | 17 | done |
18 | 18 | |
19 | +# Install production dependencies only (for runtime image) | |
20 | +FROM base AS prod-deps | |
21 | +RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* | |
22 | +COPY package.json bun.lock ./ | |
23 | +COPY cli/package.json ./cli/package.json | |
24 | +# Clear Bun install cache to avoid stale/corrupt node-gyp entries across layers | |
25 | +RUN rm -rf /root/.bun/install/cache || true | |
26 | +RUN for i in 1 2 3; do \ | |
27 | + bun install --frozen-lockfile --production && break || \ | |
28 | + (echo "prod bun install attempt $i failed, clearing cache and retrying in 10s..." && \ | |
29 | + rm -rf /root/.bun/install/cache && sleep 10); \ | |
30 | + done | |
31 | + | |
19 | 32 | # Build the application |
20 | 33 | FROM base AS builder |
21 | 34 | COPY --from=deps /app/node_modules ./node_modules |
| @@ -24,34 +37,39 @@ ENV SKIP_REDIS_CHECK=1 | ||
24 | 37 | RUN bun run build |
25 | 38 | |
26 | 39 | # Production image |
27 | -FROM oven/bun:1 AS runner | |
40 | +FROM oven/bun:1-slim AS runner | |
28 | 41 | WORKDIR /app |
29 | 42 | |
30 | -# Install git, ssh, and bash (needed for git operations and entrypoint) | |
31 | -RUN apt-get update && apt-get install -y git openssh-client bash && rm -rf /var/lib/apt/lists/* | |
43 | +# Install git, ssh, bash, and wget (git operations, entrypoint, healthcheck) | |
44 | +RUN apt-get update && apt-get install -y --no-install-recommends git openssh-client bash wget && \ | |
45 | + rm -rf /var/lib/apt/lists/* | |
32 | 46 | |
33 | 47 | # Create data directories |
34 | -RUN mkdir -p /data/repos /data/storage /data/cache /data/ssh && \ | |
48 | +RUN mkdir -p /data/repositories /data/storage /data/cache /data/ssh && \ | |
35 | 49 | chown -R bun:bun /data |
36 | 50 | |
37 | -# Copy built application | |
51 | +# Copy only what's needed for runtime | |
38 | 52 | COPY --from=builder --chown=bun:bun /app/dist ./dist |
39 | -COPY --from=builder --chown=bun:bun /app/node_modules ./node_modules | |
53 | +COPY --from=prod-deps --chown=bun:bun /app/node_modules ./node_modules | |
40 | 54 | COPY --from=builder --chown=bun:bun /app/package.json ./ |
41 | 55 | |
42 | -# Copy drizzle config and schema for migrations | |
56 | +# Copy drizzle config, committed migrations, and full source tree. | |
57 | +# The full src/ is required so raw-TS entrypoints (scripts/ssh-server.ts, | |
58 | +# scripts/worker.ts) can resolve the "@/..." path aliases via tsconfig. | |
43 | 59 | COPY --from=builder --chown=bun:bun /app/drizzle.config.ts ./ |
44 | -COPY --from=builder --chown=bun:bun /app/src/db ./src/db | |
60 | +COPY --from=builder --chown=bun:bun /app/drizzle ./drizzle | |
61 | +COPY --from=builder --chown=bun:bun /app/src ./src | |
45 | 62 | COPY --from=builder --chown=bun:bun /app/tsconfig.json ./ |
46 | 63 | |
47 | -# Copy entrypoint script | |
64 | +# Copy runtime scripts (migrate, ssh-server, worker) and entrypoint | |
65 | +COPY --from=builder --chown=bun:bun /app/scripts ./scripts | |
48 | 66 | COPY --chown=bun:bun docker-entrypoint.sh ./ |
49 | 67 | |
50 | 68 | # Set environment variables |
51 | 69 | ENV HOST=0.0.0.0 |
52 | 70 | ENV PORT=4321 |
53 | 71 | ENV DATA_DIR=/data |
54 | -ENV REPOS_PATH=/data/repos | |
72 | +ENV GIT_REPOS_PATH=/data/repositories | |
55 | 73 | ENV STORAGE_PATH=/data/storage |
56 | 74 | ENV CACHE_PATH=/data/cache |
57 | 75 | ENV SSH_PATH=/data/ssh |
58 | 76 | |
▼Dockerfile.worker
+44-0
| @@ -0,0 +1,44 @@ | ||
1 | +FROM oven/bun:1 AS base | |
2 | +WORKDIR /app | |
3 | + | |
4 | +# Install dependencies | |
5 | +FROM base AS deps | |
6 | +RUN apt-get update && apt-get install -y python3 make g++ gcc libc6-dev && rm -rf /var/lib/apt/lists/* | |
7 | +COPY package.json bun.lock ./ | |
8 | +COPY cli/package.json ./cli/package.json | |
9 | +RUN for i in 1 2 3; do \ | |
10 | + bun install --frozen-lockfile && break || \ | |
11 | + (echo "bun install attempt $i failed, retrying in 10s..." && sleep 10); \ | |
12 | + done | |
13 | + | |
14 | +# Production image | |
15 | +FROM oven/bun:1 AS runner | |
16 | +WORKDIR /app | |
17 | + | |
18 | +# Install git (needed for git operations) | |
19 | +RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* | |
20 | + | |
21 | +# Create data directories | |
22 | +RUN mkdir -p /data/repositories /data/storage /data/cache && \ | |
23 | + chown -R bun:bun /data | |
24 | + | |
25 | +# Copy node_modules and source | |
26 | +COPY --from=deps --chown=bun:bun /app/node_modules ./node_modules | |
27 | +COPY --from=deps --chown=bun:bun /app/package.json ./ | |
28 | +COPY --from=deps --chown=bun:bun /app/cli ./cli | |
29 | +COPY --from=deps --chown=bun:bun /app/tsconfig.json ./ | |
30 | + | |
31 | +# Copy source files needed for worker | |
32 | +COPY --from=deps --chown=bun:bun /app/src/lib ./src/lib | |
33 | +COPY --from=deps --chown=bun:bun /app/src/db ./src/db | |
34 | +COPY --from=deps --chown=bun:bun /app/scripts/worker.ts ./scripts/worker.ts | |
35 | + | |
36 | +# Set environment variables | |
37 | +ENV NODE_ENV=production | |
38 | +ENV DATA_DIR=/data | |
39 | + | |
40 | +# Switch to non-root user | |
41 | +USER bun | |
42 | + | |
43 | +# Start the worker | |
44 | +CMD ["bun", "scripts/worker.ts"] | |
0 | 45 | |
▼PRODUCTION_READINESS.md
+32-14
| @@ -1,6 +1,6 @@ | ||
1 | 1 | # OpenCodeHub Production Readiness Report |
2 | 2 | |
3 | -**Date:** 2026-04-30 | |
3 | +**Date:** 2026-08-02 | |
4 | 4 | **Auditor:** Agent Code Review |
5 | 5 | **Status:** Production-ready |
6 | 6 | |
| @@ -11,11 +11,25 @@ | ||
11 | 11 | 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. |
12 | 12 | |
13 | 13 | **Current State:** |
14 | -- Lint: 0 errors, 0 warnings, 491 hints | |
14 | +- Lint: 0 errors, 0 warnings | |
15 | 15 | - Typecheck: 0 errors |
16 | -- Tests: 546 passing, 0 failing (114 test files, 100% pass rate) | |
17 | -- Security: Rate limiting, CSRF, JWT, env validation, secret encryption all implemented | |
18 | -- Docker: Full docker-compose setup with PostgreSQL, Redis, and CI runner | |
16 | +- Tests: 546 passing, 0 failing (100% pass rate) | |
17 | +- Security: Rate limiting, CSRF, JWT, env validation, secret encryption, authenticated metrics all implemented | |
18 | +- Docker: Production-safe compose stacks (no default secrets, migrations applied at boot, worker + SSH services included) | |
19 | +- Storage: `local` (filesystem/NAS) and `s3` (any S3-compatible store) only | |
20 | +- Migrations: Versioned `drizzle/` folder committed and applied deterministically at container boot | |
21 | + | |
22 | +## Recent Production Hardening | |
23 | + | |
24 | +| Area | Change | | |
25 | +|------|--------| | |
26 | +| Secrets | `.env.backup-*` removed from git tracking; docker-compose no longer ships default secrets | | |
27 | +| Migrations | Committed `drizzle/0000_*.sql`; applied at boot via `scripts/migrate.ts` (no runtime `generate`) | | |
28 | +| `npm start` | Now runs the built server (`node ./dist/server/entry.mjs`) instead of `astro dev` | | |
29 | +| Docker | `ssh-git` and `worker` services now run the correct process (`PROCESS_TYPE`); image copies `scripts/` + `drizzle/` | | |
30 | +| Metrics | `GET /api/metrics` protected by optional `METRICS_TOKEN` bearer auth | | |
31 | +| Env vars | Standardized on `DATABASE_DRIVER`, `GIT_REPOS_PATH`, `GIT_SSH_HOST_KEY`, `STORAGE_PATH` across all compose files | | |
32 | +| Storage docs | Removed all references to removed backends (GDrive/Azure/GCS/etc.); only `local` + `s3` documented | | |
19 | 33 | |
20 | 34 | --- |
21 | 35 | |
| @@ -90,14 +104,18 @@ DATABASE_DRIVER=postgres | ||
90 | 104 | DATABASE_URL=postgresql://user:pass@localhost:5432/opencodehub |
91 | 105 | |
92 | 106 | # Redis (required for distributed rate limiting, sessions, and queues) |
93 | -REDIS_URL=redis://localhost:6379 | |
94 | - | |
95 | -# Storage (use S3/GCS/Azure in production, not local) | |
96 | -STORAGE_DRIVER=s3 | |
97 | -S3_BUCKET=your-bucket | |
98 | -S3_REGION=us-east-1 | |
99 | -S3_ACCESS_KEY= | |
100 | -S3_SECRET_KEY= | |
107 | +REDIS_URL=redis://:password@localhost:6379 | |
108 | + | |
109 | +# Storage — only local and s3 are supported (local is ideal for a NAS/dedicated disk) | |
110 | +STORAGE_TYPE=local | |
111 | +STORAGE_PATH=/mnt/nas/opencodehub/storage | |
112 | + | |
113 | +# S3-compatible (AWS S3, MinIO, R2, Garage, ...) when STORAGE_TYPE=s3: | |
114 | +STORAGE_BUCKET=your-bucket | |
115 | +STORAGE_REGION=us-east-1 | |
116 | +STORAGE_ENDPOINT= | |
117 | +STORAGE_ACCESS_KEY_ID= | |
118 | +STORAGE_SECRET_ACCESS_KEY= | |
101 | 119 | ``` |
102 | 120 | |
103 | 121 | ### Docker Compose (Recommended) |
| @@ -128,7 +146,7 @@ Exposes: | ||
128 | 146 | |
129 | 147 | 1. **Modular Monolith** — Easy to deploy as single container or scale individual workers |
130 | 148 | 2. **Database Flexibility** — PostgreSQL for production, SQLite/Turso for dev/edge |
131 | -3. **Storage Abstraction** — 8+ backends via adapter pattern (S3, GCS, Azure, Google Drive, OneDrive, Dropbox, FTP, rclone) | |
149 | +3. **Storage Abstraction** — two backends via adapter pattern: `local` (filesystem; ideal for a dedicated disk or NAS mount) and `s3` (any S3-compatible store) | |
132 | 150 | 4. **Git Protocol Integration** — Native HTTP + SSH via `git` CLI and `ssh2` |
133 | 151 | 5. **CI/CD Engine** — GitHub Actions-compatible with Docker-based execution |
134 | 152 | 6. **Stacked PRs** — Graphite-style workflows in web + CLI |
135 | 153 | |
▼README.md
+132-372
| @@ -1,242 +1,137 @@ | ||
1 | -# OpenCodeHub | |
2 | - | |
3 | 1 | <p align="center"> |
4 | 2 | <picture> |
5 | 3 | <source media="(prefers-color-scheme: dark)" srcset="public/logo-dark.png"> |
6 | 4 | <source media="(prefers-color-scheme: light)" srcset="public/logo-light.png"> |
7 | - <img src="public/logo-light.png" alt="OpenCodeHub Logo" width="420" /> | |
5 | + <img src="public/logo-light.png" alt="OpenCodeHub" width="420" /> | |
8 | 6 | </picture> |
9 | 7 | </p> |
10 | 8 | |
11 | -<p align="center"> | |
12 | - <strong>A modern, self-hosted Git platform for teams that want speed, control, and clean workflows.</strong> | |
13 | -</p> | |
9 | +<h3 align="center">The self-hosted Git platform that doesn't compromise.</h3> | |
14 | 10 | |
15 | 11 | <p align="center"> |
16 | - <a href="https://www.npmjs.com/package/opencodehub-cli"><img src="https://img.shields.io/npm/v/opencodehub-cli?style=flat-square&label=CLI" alt="CLI version"></a> | |
17 | - <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License"></a> | |
18 | - <a href="docs/index.md"><img src="https://img.shields.io/badge/docs-available-success?style=flat-square" alt="Docs"></a> | |
19 | - <a href="https://github.com/swadhinbiswas/OpencodeHub/actions"><img src="https://img.shields.io/badge/CI-passing-success?style=flat-square" alt="CI"></a> | |
12 | + <a href="https://github.com/swadhinbiswas/OpencodeHub/actions"><img src="https://img.shields.io/badge/CI-passing-brightgreen?style=for-the-badge" alt="CI"></a> | |
13 | + <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=for-the-badge" alt="License"></a> | |
14 | + <a href="https://www.npmjs.com/package/opencodehub-cli"><img src="https://img.shields.io/npm/v/opencodehub-cli?style=for-the-badge&label=CLI" alt="CLI version"></a> | |
15 | + <a href="https://docs.opencodehub.space"><img src="https://img.shields.io/badge/docs-opencodehub.space-brightgreen?style=for-the-badge" alt="Docs"></a> | |
16 | + <a href="https://hub.docker.com/r/opencodehub/opencodehub"><img src="https://img.shields.io/badge/docker-opencodehub%2Fopencodehub-blue?style=for-the-badge&logo=docker" alt="Docker"></a> | |
20 | 17 | </p> |
21 | 18 | |
22 | 19 | --- |
23 | 20 | |
24 | -## Table of Contents | |
21 | +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. | |
25 | 22 | |
26 | -- [Why OpenCodeHub](#why-opencodehub) | |
27 | -- [Features](#features) | |
28 | -- [Quick Start](#quick-start) | |
29 | -- [Architecture](#architecture) | |
30 | -- [Documentation](#documentation) | |
31 | -- [CLI](#cli) | |
32 | -- [API](#api) | |
33 | -- [Security](#security) | |
34 | -- [Deployment](#deployment) | |
35 | -- [Contributing](#contributing) | |
36 | -- [License](#license) | |
23 | +**[Documentation](https://docs.opencodehub.space)** · **[Deploy in 5 minutes](#deploy)** · **[CLI Reference](https://docs.opencodehub.space/reference/cli-commands/)** | |
37 | 24 | |
38 | 25 | --- |
39 | 26 | |
40 | -## Why OpenCodeHub | |
27 | +## Demo | |
28 | + | |
29 | +<!-- Replace VIDEO_ID with your YouTube video ID --> | |
30 | +[](https://youtu.be/VIDEO_ID) | |
41 | 31 | |
42 | -Most teams eventually hit the same pain points: | |
32 | +*Watch the full walkthrough — deployment, stacked PRs, AI review, and merge queue in action.* | |
43 | 33 | |
44 | -- **PRs are too large to review quickly** — Stacked PRs let you break changes into reviewable pieces | |
45 | -- **Tooling is split across many services** — One platform for Git, PRs, CI/CD, issues, and wiki | |
46 | -- **Data residency and security requirements** — Self-hosted by default, you control everything | |
47 | -- **Merge queue complexity** — Stack-aware merge queue with speculative builds | |
48 | -- **Slow review cycles** — AI-powered code review catches issues before human review | |
34 | +--- | |
35 | + | |
36 | +## Why OpenCodeHub? | |
49 | 37 | |
50 | -OpenCodeHub is built for that reality. | |
38 | +| Problem | Solution | | |
39 | +|---------|----------| | |
40 | +| PRs too large to review | **Stacked PRs** — break changes into small, dependent branches | | |
41 | +| Merge conflicts on main | **Merge Queue** — stack-aware ordering with speculative CI builds | | |
42 | +| Slow review cycles | **AI Code Review** — catches bugs before humans even look | | |
43 | +| Split across 5+ services | **All-in-one** — Git, PRs, CI/CD, issues, wiki in one place | | |
44 | +| Data leaves your servers | **Self-hosted** — your code stays on your hardware | | |
51 | 45 | |
52 | 46 | --- |
53 | 47 | |
54 | 48 | ## Features |
55 | 49 | |
56 | 50 | ### Core Platform |
57 | - | |
58 | -| Feature | Status | Description | | |
59 | -|---------|--------|-------------| | |
60 | -| **Git Hosting** | Ready | HTTP (Smart Protocol) + SSH git push/pull | | |
61 | -| **Pull Requests** | Ready | Comments, reviews, drafts, approvals, suggested changes | | |
62 | -| **Issues** | Ready | Labels, milestones, project boards | | |
63 | -| **Wiki** | Ready | Repository wiki with revision history | | |
64 | -| **Organizations** | Ready | Teams, collaborators, repository settings | | |
65 | -| **Branch Protection** | Ready | Required reviews, status checks, push restrictions | | |
51 | +- **Git Hosting** — HTTP smart protocol + SSH push/pull, forks, mirroring, LFS | |
52 | +- **Pull Requests** — Inline comments, approvals, suggested changes, draft PRs | |
53 | +- **Issues & Projects** — Labels, milestones, custom fields, kanban boards | |
54 | +- **Wiki** — Repository wiki with revision history | |
55 | +- **Organizations** — Teams, collaborators, role-based access control | |
66 | 56 | |
67 | 57 | ### Delivery Workflows |
68 | - | |
69 | -| Feature | Status | Description | | |
70 | -|---------|--------|-------------| | |
71 | -| **Stacked PRs** | Ready | Graphite-style stack workflows in web + CLI | | |
72 | -| **Merge Queue** | Ready | Stack-aware queue with speculative builds and priority lanes | | |
73 | -| **CI/CD Pipelines** | Ready | GitHub Actions-compatible engine + Docker-based runner | | |
74 | -| **Webhooks** | Ready | Outbound webhooks with event filtering | | |
75 | -| **Automations** | Ready | Workflow automation rules | | |
76 | - | |
77 | -### Security & Governance | |
78 | - | |
79 | -| Feature | Status | Description | | |
80 | -|---------|--------|-------------| | |
81 | -| **Authentication** | Ready | JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP | | |
82 | -| **Authorization** | Ready | RBAC with roles, team permissions, collaborator levels | | |
83 | -| **Rate Limiting** | Ready | Redis-backed per-endpoint rate limiting | | |
84 | -| **CSRF Protection** | Ready | Double-submit cookie pattern | | |
85 | -| **Secret Scanning** | Ready | Detect secrets in commits | | |
86 | -| **Audit Logging** | Ready | Track all administrative actions | | |
58 | +- **Stacked PRs** — Graphite-style stacked branches with web + CLI support | |
59 | +- **Merge Queue** — Stack-aware queue with speculative builds and priority lanes | |
60 | +- **CI/CD Pipelines** — GitHub Actions-compatible engine with Docker-based runners | |
61 | +- **Webhooks** — Outbound webhooks with event filtering and HMAC signing | |
62 | +- **Automations** — Rule-based workflow automation for PRs and deployments | |
63 | + | |
64 | +### AI & Quality | |
65 | +- **AI Code Review** — 10+ providers: GPT-4, Claude, Gemini, Groq, Ollama, OpenRouter | |
66 | +- **Secret Scanning** — Detect secrets in commits before they reach production | |
67 | +- **Branch Protection** — Required reviews, status checks, push restrictions | |
68 | +- **Developer Metrics** — PR velocity, review efficiency, time-to-merge tracking | |
69 | + | |
70 | +### Security | |
71 | +- **Authentication** — JWT sessions, OAuth (GitHub, Google, GitLab), 2FA/TOTP, SSO/SAML | |
72 | +- **Authorization** — RBAC with roles, team permissions, collaborator levels | |
73 | +- **Rate Limiting** — Redis-backed per-endpoint rate limiting | |
74 | +- **Audit Logging** — Track all administrative actions | |
87 | 75 | |
88 | 76 | ### Extensibility |
89 | - | |
90 | -| Feature | Status | Description | | |
91 | -|---------|--------|-------------| | |
92 | -| **REST API** | Ready | 140+ API routes | | |
93 | -| **GraphQL** | Ready | Full GraphQL endpoint | | |
94 | -| **OpenAPI** | Ready | Auto-generated OpenAPI spec | | |
95 | -| **CLI** | Ready | 20+ command groups (`opencodehub-cli`) | | |
96 | -| **Storage Adapters** | Ready | 8+ backends (S3, GCS, Azure, Google Drive, etc.) | | |
77 | +- **REST API** — 175+ endpoints covering all platform features | |
78 | +- **GraphQL** — Full GraphQL endpoint for flexible queries | |
79 | +- **CLI** — `och` command line tool with 20+ command groups | |
80 | +- **Storage** — Local, S3, MinIO, R2, or any S3-compatible backend | |
97 | 81 | |
98 | 82 | --- |
99 | 83 | |
100 | -## Quick Start | |
101 | - | |
102 | -### Prerequisites | |
84 | +## Deploy | |
103 | 85 | |
104 | -- Node.js >= 20 | |
105 | -- npm or bun | |
106 | -- git | |
107 | -- PostgreSQL 14+ (recommended) or SQLite (dev) | |
108 | - | |
109 | -### Docker (Fastest) | |
86 | +### Docker (Recommended) | |
110 | 87 | |
111 | 88 | ```bash |
112 | -# 1. Clone | |
113 | 89 | git clone https://github.com/swadhinbiswas/OpencodeHub.git |
114 | 90 | cd OpenCodeHub |
115 | - | |
116 | -# 2. Configure | |
117 | 91 | cp .env.example .env |
118 | -# Edit .env with your settings | |
119 | - | |
120 | -# 3. Start | |
121 | -docker-compose up -d | |
122 | - | |
123 | -# 4. Create admin user | |
124 | -docker-compose exec app bun run scripts/seed-admin.ts | |
125 | - | |
126 | -# 5. Open | |
127 | -open http://localhost:4321 | |
92 | +docker compose up -d | |
93 | +docker compose exec app bun run scripts/seed-admin.ts | |
128 | 94 | ``` |
129 | 95 | |
130 | -### NAS & Edge Deployment (Cloudflare / Tailscale) | |
96 | +Open **http://localhost:4321** and create your admin account. | |
131 | 97 | |
132 | -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. | |
98 | +### Render (Free) | |
133 | 99 | |
134 | -```bash | |
135 | -# Expose via Cloudflare Tunnel | |
136 | -TUNNEL_TOKEN=your_token docker-compose -f docker-compose.nas.yml --profile cloudflare up -d | |
137 | - | |
138 | -# OR Expose via Tailscale | |
139 | -TS_AUTHKEY=your_key docker-compose -f docker-compose.nas.yml --profile tailscale up -d | |
140 | -``` | |
141 | -See the [NAS Deployment Guide](docs/administration/deploy-nas.md) and [Exposing NAS Guide](docs/administration/expose-nas.md) for full details. | |
142 | - | |
143 | - | |
144 | -### Manual Setup | |
100 | +Deploy to Render with free PostgreSQL + Upstash Redis: | |
145 | 101 | |
146 | 102 | ```bash |
147 | -# 1. Install dependencies | |
148 | -npm install | |
149 | - | |
150 | -# 2. Configure environment | |
151 | -cp .env.example .env | |
152 | -# Edit .env (SQLite defaults work for dev) | |
153 | - | |
154 | -# 3. Initialize database | |
155 | -npm run db:push | |
156 | - | |
157 | -# 4. Create admin user | |
158 | -bun run scripts/seed-admin.ts | |
159 | - | |
160 | -# 5. Start development server | |
161 | -npm run dev | |
103 | +# 1. Create free Redis at upstash.com (Singapore region) | |
104 | +# 2. Push to GitHub | |
105 | +# 3. Render → New → Blueprint → Select your repo | |
106 | +# 4. Set REDIS_URL and SITE_URL | |
107 | +# 5. Deploy | |
162 | 108 | ``` |
163 | 109 | |
164 | -App URL: `http://localhost:3000` (dev) or `http://localhost:4321` (Docker) | |
110 | +See the [Render Deployment Guide](docs/RENDER-DEPLOYMENT.md) for step-by-step instructions. | |
165 | 111 | |
112 | +### More Deployment Options | |
166 | 113 | |
167 | -## Architecture | |
168 | - | |
169 | -``` | |
170 | -Clients (Browser / Git CLI / och CLI) | |
171 | - | | |
172 | - v | |
173 | -OpenCodeHub Platform (Astro SSR + React) | |
174 | - |-- Web UI | |
175 | - |-- REST API (140+ routes) | |
176 | - |-- GraphQL Endpoint | |
177 | - |-- Git HTTP Server | |
178 | - |-- SSH Git Server (port 2222) | |
179 | - | | |
180 | - v | |
181 | -Persistence Layer | |
182 | - |-- PostgreSQL / SQLite / Turso (Drizzle ORM) | |
183 | - |-- Redis (sessions, queues, caching) | |
184 | - |-- Pluggable Storage (S3, GCS, Azure, local, etc.) | |
185 | -``` | |
186 | - | |
187 | -### Runtime Processes | |
188 | - | |
189 | -| Process | Command | Purpose | | |
190 | -|---------|---------|---------| | |
191 | -| Main App | `npm run dev` | Web UI + API + Git HTTP | | |
192 | -| SSH Git | `npm run git:start` | SSH git push/pull | | |
193 | -| Worker | `npm run worker:start` | Background jobs | | |
194 | -| Runner | `npm run runner:start` | CI/CD pipeline execution | | |
195 | - | |
196 | -### Tech Stack | |
197 | - | |
198 | -| Layer | Technology | | |
199 | -|-------|-----------| | |
200 | -| Framework | Astro 4.x (SSR, Node standalone) | | |
201 | -| UI | React 18 + Tailwind CSS + Radix UI | | |
202 | -| Database | Drizzle ORM (PostgreSQL, SQLite, Turso) | | |
203 | -| Auth | JWT (jose) + bcryptjs + TOTP (otplib) + OAuth (arctic) | | |
204 | -| Git | Native git CLI + simple-git + isomorphic-git | | |
205 | -| SSH | ssh2 library | | |
206 | -| CI/CD | Dockerode (Docker API) | | |
207 | -| Storage | Abstract adapter pattern (8+ backends) | | |
208 | -| Queue | BullMQ + Redis | | |
209 | -| CLI | Commander.js + Inquirer + simple-git | | |
210 | - | |
211 | - | |
212 | -## Documentation | |
213 | - | |
214 | -Comprehensive documentation is available in the `docs/` directory: | |
215 | - | |
216 | -- **[Getting Started](docs/getting-started/installation.md)** — Installation and setup | |
217 | -- **[Features](docs/features/)** — Stacked PRs, AI review, merge queue, CI/CD | |
218 | -- **[Guides](docs/guides/)** — Team workflows, branch protection, webhooks | |
219 | -- **[API Reference](docs/api/rest-api.md)** — REST API documentation | |
220 | -- **[Administration](docs/administration/)** — Deployment, security, monitoring | |
221 | -- **[Development](docs/development/)** — Architecture, contributing, testing | |
222 | -- **[Tutorials](docs/tutorials/)** — Hands-on guides | |
114 | +| Platform | Guide | Cost | | |
115 | +|----------|-------|------| | |
116 | +| Docker Compose | [Deployment Guide](docs/administration/deployment.md) | Free | | |
117 | +| Render (Asia) | [Render Guide](docs/RENDER-DEPLOYMENT.md) | Free | | |
118 | +| Oracle Cloud | [Free Deployment](docs/FREE-DEPLOYMENT.md) | Free forever | | |
119 | +| NAS (Synology/TrueNAS) | [NAS Guide](docs/administration/deploy-nas.md) | Free | | |
120 | +| Kubernetes | [K8s Guide](docs/administration/kubernetes.md) | Free | | |
121 | +| Cloudflare Tunnel | [Cloudflare Guide](docs/administration/deploy-cloudflare.md) | Free | | |
223 | 122 | |
224 | 123 | --- |
225 | 124 | |
226 | 125 | ## CLI |
227 | 126 | |
228 | -The companion CLI is published as `opencodehub-cli`: | |
229 | - | |
230 | 127 | ```bash |
231 | 128 | # Install |
232 | 129 | npm install -g opencodehub-cli |
233 | 130 | |
234 | -# Authenticate | |
131 | +# Login | |
235 | 132 | och auth login --url http://localhost:4321 |
236 | 133 | |
237 | -# Stack workflow | |
134 | +# Stacked PR workflow | |
238 | 135 | och stack create feature/auth-step-1 |
239 | 136 | och stack submit |
240 | 137 | och stack sync |
| @@ -249,247 +144,114 @@ och queue add <pr-number> | ||
249 | 144 | och focus |
250 | 145 | ``` |
251 | 146 | |
252 | -[Full CLI Reference](docs/reference/cli-commands.md) | |
147 | +[Full CLI Reference](https://docs.opencodehub.space/reference/cli-commands/) | |
253 | 148 | |
254 | 149 | --- |
255 | 150 | |
256 | 151 | ## API |
257 | 152 | |
258 | -OpenCodeHub provides multiple API interfaces: | |
259 | - | |
260 | -### REST API | |
261 | - | |
262 | -140+ REST endpoints covering all platform features: | |
263 | - | |
264 | 153 | ```bash |
265 | -# Authentication | |
266 | -curl -X POST http://localhost:4321/api/auth/login \ | |
267 | - -H "Content-Type: application/json" \ | |
268 | - -d '{"login":"alice","password":"secret"}' | |
269 | - | |
270 | -# Create repository | |
154 | +# Create a repository | |
271 | 155 | curl -X POST http://localhost:4321/api/repos \ |
272 | - -H "Authorization: Bearer <token>" \ | |
156 | + -H "Authorization: Bearer YOUR_TOKEN" \ | |
273 | 157 | -H "Content-Type: application/json" \ |
274 | 158 | -d '{"name":"my-project","visibility":"public"}' |
275 | 159 | |
276 | 160 | # List pull requests |
277 | -curl http://localhost:4321/api/repos/alice/my-project/pulls \ | |
278 | - -H "Authorization: Bearer <token>" | |
279 | -``` | |
161 | +curl http://localhost:4321/api/repos/owner/repo/pulls \ | |
162 | + -H "Authorization: Bearer YOUR_TOKEN" | |
280 | 163 | |
281 | -### GraphQL | |
282 | - | |
283 | -```bash | |
164 | +# GraphQL | |
284 | 165 | curl -X POST http://localhost:4321/api/graphql \ |
285 | - -H "Authorization: Bearer <token>" \ | |
166 | + -H "Authorization: Bearer YOUR_TOKEN" \ | |
286 | 167 | -H "Content-Type: application/json" \ |
287 | 168 | -d '{"query": "{ repositories { name owner { username } } }"}' |
288 | 169 | ``` |
289 | 170 | |
290 | -### OpenAPI Spec | |
291 | - | |
292 | -The OpenAPI spec is available at: | |
293 | -``` | |
294 | -GET /api/openapi.json | |
295 | -``` | |
171 | +[Full API Reference](https://docs.opencodehub.space/api/rest-api/) | |
296 | 172 | |
297 | 173 | --- |
298 | 174 | |
299 | -## Security | |
175 | +## Tech Stack | |
300 | 176 | |
301 | -OpenCodeHub implements comprehensive security controls: | |
302 | - | |
303 | -- **Authentication**: JWT sessions with secure cookies, OAuth 2.0, 2FA/TOTP | |
304 | -- **Authorization**: Role-based access control (RBAC), repository permissions, team permissions | |
305 | -- **Data Protection**: AES-256-GCM encryption for workflow secrets and AI config | |
306 | -- **Transport**: HTTPS enforcement in production, TLS 1.2+ | |
307 | -- **CSRF**: Double-submit cookie pattern for state-changing requests | |
308 | -- **Rate Limiting**: Redis-backed per-endpoint rate limiting | |
309 | -- **Input Validation**: Zod schema validation on all API inputs | |
310 | -- **Secret Scanning**: Detect and block secrets in commits | |
311 | -- **SSRF Protection**: Validate webhook URLs against private IP ranges | |
312 | - | |
313 | -See [Security Best Practices](docs/administration/security.md) for detailed configuration. | |
177 | +| Layer | Technology | | |
178 | +|-------|-----------| | |
179 | +| Framework | Astro 4.x (SSR) + React 18 | | |
180 | +| UI | Tailwind CSS + Radix UI | | |
181 | +| Database | PostgreSQL / SQLite / Turso (Drizzle ORM) | | |
182 | +| Auth | JWT + OAuth + 2FA/TOTP + SSO/SAML | | |
183 | +| Git | Native git CLI + simple-git + isomorphic-git | | |
184 | +| SSH | ssh2 library | | |
185 | +| CI/CD | Docker-based runners, GitHub Actions syntax | | |
186 | +| Storage | Local filesystem or S3-compatible (AWS, MinIO, R2) | | |
187 | +| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter | | |
188 | +| Queue | BullMQ + Redis | | |
189 | +| CLI | Commander.js + Inquirer | | |
314 | 190 | |
315 | 191 | --- |
316 | 192 | |
317 | -## Deployment | |
318 | - | |
319 | -### Docker Compose (Recommended) | |
320 | - | |
321 | -The included `docker-compose.yml` spins up: | |
322 | -- OpenCodeHub app (port 4321) | |
323 | -- PostgreSQL database | |
324 | -- Redis cache | |
325 | -- CI runner (Docker-in-Docker) | |
326 | -- Optional: MinIO for S3-compatible storage | |
193 | +## Architecture | |
327 | 194 | |
328 | -```bash | |
329 | -docker-compose up -d | |
330 | 195 | ``` |
331 | - | |
332 | -### Production Checklist | |
333 | - | |
334 | -Before deploying to production: | |
335 | - | |
336 | -1. **Change ALL secrets**: | |
337 | - ```bash | |
338 | - JWT_SECRET=$(openssl rand -hex 32) | |
339 | - SESSION_SECRET=$(openssl rand -hex 32) | |
340 | - INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) | |
341 | - CRON_SECRET=$(openssl rand -hex 32) | |
342 | - RUNNER_SECRET=$(openssl rand -hex 32) | |
343 | - WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) | |
344 | - ``` | |
345 | - | |
346 | -2. **Use PostgreSQL** (not SQLite) | |
347 | -3. **Configure HTTPS** with reverse proxy (Nginx/Caddy) | |
348 | -4. **Set up Redis** for distributed sessions and rate limiting | |
349 | -5. **Configure external storage** (S3/GCS/Azure instead of local) | |
350 | -6. **Enable rate limiting**: `RATE_LIMIT_ENABLED=true` | |
351 | -7. **Set up SMTP** for email notifications | |
352 | -8. **Configure backups** for PostgreSQL data | |
353 | - | |
354 | -See [Deployment Guide](docs/administration/deployment.md) for detailed instructions. | |
355 | - | |
356 | - | |
357 | -## Database | |
358 | - | |
359 | -OpenCodeHub uses Drizzle ORM with support for multiple database engines: | |
360 | - | |
361 | -| Engine | Driver | Use Case | | |
362 | -|--------|--------|----------| | |
363 | -| **PostgreSQL** | `pg` | Production (recommended) | | |
364 | -| **SQLite** | `better-sqlite3` | Development, small deployments | | |
365 | -| **Turso/LibSQL** | `@libsql/client` | Edge deployments, Vercel | | |
366 | - | |
367 | -```bash | |
368 | -# Generate migrations | |
369 | -npm run db:generate | |
370 | - | |
371 | -# Apply migrations | |
372 | -npm run db:migrate | |
373 | - | |
374 | -# Push schema (dev) | |
375 | -npm run db:push | |
376 | - | |
377 | -# Open Drizzle Studio | |
378 | -npm run db:studio | |
196 | +┌─────────────────────────────────────────────────────────────┐ | |
197 | +│ CLIENTS │ | |
198 | +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ | |
199 | +└─────────────────────────────────────────────────────────────┘ | |
200 | + │ | |
201 | +┌─────────────────────────────────────────────────────────────┐ | |
202 | +│ OPENCODEHUB PLATFORM │ | |
203 | +│ Web UI (Astro+React) │ REST API (175+ routes) │ GraphQL │ | |
204 | +│ Git Server (HTTP) │ SSH Server (ssh2) │ │ | |
205 | +│ Pipeline Runner (Docker) │ │ | |
206 | +└─────────────────────────────────────────────────────────────┘ | |
207 | + │ | |
208 | +┌─────────────────────────────────────────────────────────────┐ | |
209 | +│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │ | |
210 | +└─────────────────────────────────────────────────────────────┘ | |
379 | 211 | ``` |
380 | 212 | |
381 | 213 | --- |
382 | 214 | |
383 | -## Storage | |
384 | - | |
385 | -All blob storage goes through an abstract `StorageAdapter`: | |
386 | - | |
387 | -| Backend | Adapter | Use Case | | |
388 | -|---------|---------|----------| | |
389 | -| **Local** | `LocalStorageAdapter` | Development, single-node | | |
390 | -| **S3** | `S3StorageAdapter` | Production (AWS, MinIO, R2) | | |
391 | -| **Google Cloud** | `GCSStorageAdapter` | GCP deployments | | |
392 | -| **Azure Blob** | `AzureStorageAdapter` | Azure deployments | | |
393 | -| **Google Drive** | `GoogleDriveStorageAdapter` | Personal/small team | | |
394 | -| **OneDrive** | `OneDriveStorageAdapter` | Microsoft ecosystem | | |
395 | -| **Dropbox** | `DropboxStorageAdapter` | Simple cloud storage | | |
396 | -| **Rclone** | `RcloneStorageAdapter` | Any rclone-compatible remote | | |
215 | +## Documentation | |
397 | 216 | |
398 | -Configure via `STORAGE_DRIVER` environment variable. | |
217 | +| Topic | Link | | |
218 | +|-------|------| | |
219 | +| Installation | [docs.opencodehub.space/getting-started/installation](https://docs.opencodehub.space/getting-started/installation/) | | |
220 | +| Configuration | [docs.opencodehub.space/administration/configuration](https://docs.opencodehub.space/administration/configuration/) | | |
221 | +| Stacked PRs | [docs.opencodehub.space/features/stacked-prs](https://docs.opencodehub.space/features/stacked-prs/) | | |
222 | +| AI Code Review | [docs.opencodehub.space/features/ai-review](https://docs.opencodehub.space/features/ai-review/) | | |
223 | +| CLI Reference | [docs.opencodehub.space/reference/cli-commands](https://docs.opencodehub.space/reference/cli-commands/) | | |
224 | +| API Reference | [docs.opencodehub.space/api/rest-api](https://docs.opencodehub.space/api/rest-api/) | | |
225 | +| Deployment | [docs.opencodehub.space/administration/deployment](https://docs.opencodehub.space/administration/deployment/) | | |
399 | 226 | |
400 | 227 | --- |
401 | 228 | |
402 | -## Development | |
229 | +## Contributing | |
403 | 230 | |
404 | 231 | ```bash |
405 | -# Setup | |
232 | +# Clone and setup | |
233 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
234 | +cd OpenCodeHub | |
406 | 235 | cp .env.example .env |
407 | 236 | npm install |
408 | 237 | npm run db:push |
409 | 238 | bun run scripts/seed-admin.ts |
410 | 239 | |
411 | -# Dev server | |
412 | -npm run dev # Web UI + API (port 3000) | |
413 | -npm run git:start # SSH git server (port 2222) | |
414 | -npm run worker:start # Background worker | |
415 | -npm run runner:start # CI runner | |
240 | +# Start development | |
241 | +npm run dev | |
416 | 242 | |
417 | -# Quality | |
418 | -npm run lint | |
419 | -npm run typecheck | |
243 | +# Run tests | |
420 | 244 | npm run test |
421 | -npm run test:coverage | |
422 | - | |
423 | -# Database | |
424 | -npm run db:generate | |
425 | -npm run db:migrate | |
426 | -npm run db:push | |
427 | -npm run db:studio | |
428 | -``` | |
429 | - | |
430 | -See [Contributing Guide](CONTRIBUTING.md) and [Development Docs](docs/development/). | |
431 | - | |
432 | - | |
433 | -## Project Structure | |
434 | - | |
435 | -``` | |
436 | -OpenCodeHub/ | |
437 | -├── src/ # Main application | |
438 | -│ ├── pages/ # Astro routes (UI + API) | |
439 | -│ ├── lib/ # Core business logic (120+ modules) | |
440 | -│ ├── db/ # Database schema and connection | |
441 | -│ ├── components/ # React components | |
442 | -│ ├── middleware.ts # Global middleware | |
443 | -│ └── runner/ # CI runner | |
444 | -├── cli/ # CLI package (opencodehub-cli) | |
445 | -│ └── src/commands/ # CLI command groups | |
446 | -├── packages/ # Additional packages | |
447 | -│ ├── git-rpc-daemon/ | |
448 | -│ ├── merge-queue-daemon/ | |
449 | -│ ├── ci-runner/ | |
450 | -│ └── sdk/ | |
451 | -├── docs/ # Documentation | |
452 | -├── docs-site/ # Documentation website | |
453 | -├── scripts/ # Utility scripts | |
454 | -├── docker-compose.yml # Docker Compose setup | |
455 | -├── Dockerfile # Main app container | |
456 | -└── package.json | |
457 | -``` | |
458 | - | |
459 | - | |
460 | -## Testing | |
461 | - | |
462 | -- **Unit Tests**: Vitest (`*.test.ts`) | |
463 | -- **E2E Tests**: Playwright | |
464 | -- **Load Tests**: Custom load tests in `tests/load/` | |
465 | -- **Accessibility**: `@axe-core/playwright` | |
466 | - | |
467 | -```bash | |
468 | -npm run test # Run all tests | |
469 | -npm run test:coverage # Run with coverage | |
470 | 245 | ``` |
471 | 246 | |
472 | - | |
473 | -## Contributing | |
474 | - | |
475 | -Contributions are welcome! | |
476 | - | |
477 | -1. Fork the repository | |
478 | -2. Create a feature branch | |
479 | -3. Run lint and tests | |
480 | -4. Open a pull request | |
481 | - | |
482 | 247 | See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and standards. |
483 | 248 | |
484 | 249 | --- |
485 | 250 | |
486 | 251 | ## Community |
487 | 252 | |
488 | -- **Documentation**: [docs/index.md](docs/index.md) | |
253 | +- **GitHub**: [github.com/swadhinbiswas/OpencodeHub](https://github.com/swadhinbiswas/OpencodeHub) | |
254 | +- **Documentation**: [docs.opencodehub.space](https://docs.opencodehub.space) | |
489 | 255 | - **Issues**: [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) |
490 | 256 | - **Discussions**: [GitHub Discussions](https://github.com/swadhinbiswas/OpencodeHub/discussions) |
491 | 257 | |
| @@ -497,13 +259,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development workflow and standards. | ||
497 | 259 | |
498 | 260 | ## License |
499 | 261 | |
500 | -MIT. See [LICENSE](LICENSE). | |
501 | - | |
502 | - | |
503 | -<p align="center"> | |
504 | - <strong>Ready to get started?</strong><br> | |
505 | - <a href="docs/getting-started/installation.md">Install OpenCodeHub</a> | | |
506 | - <a href="docs/getting-started/quick-start.md">Quick Start</a> | | |
507 | - <a href="docs/features/stacked-prs.md">Learn Stacked PRs</a> | |
508 | -</p> | |
262 | +[MIT](LICENSE) — Use it however you want. | |
509 | 263 | |
▼Vercel Deploy.md
+127-579
| @@ -1,13 +1,23 @@ | ||
1 | -# Deploy OpenCodeHub to Vercel | |
1 | +# Deploy OpenCodeHub to Vercel (Edge) | |
2 | 2 | |
3 | -> Complete guide to deploying OpenCodeHub on Vercel with Turso, Google Drive, and Redis | |
3 | +> Complete guide to deploying OpenCodeHub on Vercel with Turso (LibSQL), | |
4 | +> S3-compatible storage, and Upstash Redis. | |
4 | 5 | |
5 | -This guide walks you through deploying OpenCodeHub to Vercel using: | |
6 | -- **Turso** - Serverless SQLite database (edge-compatible) | |
7 | -- **Google Drive API** - File storage for repositories | |
8 | -- **Upstash Redis** - Serverless Redis for sessions and caching | |
6 | +OpenCodeHub is configured entirely through environment variables, so an | |
7 | +edge/Serverless deployment is just a matter of pointing the built-in | |
8 | +database, storage, and Redis drivers at managed services. The codebase | |
9 | +handles all three driver families — no custom code is needed. | |
9 | 10 | |
10 | -**Estimated time:** 30-45 minutes | |
11 | +- **Turso (LibSQL)** — edge-compatible SQLite database | |
12 | +- **S3-compatible storage** — Cloudflare R2 (free egress, recommended), | |
13 | + MinIO, AWS S3, Garage, etc. | |
14 | +- **Upstash Redis** — serverless Redis for sessions, rate limiting, queues | |
15 | + | |
16 | +> **Storage note:** only `local` and `s3` backends are supported. | |
17 | +> Google Drive, OneDrive, Dropbox, GCS, Azure, and FTP backends were | |
18 | +> removed. For Serverless, always use an S3-compatible store (`STORAGE_TYPE=s3`). | |
19 | + | |
20 | +**Estimated time:** 30-45 minutes | |
11 | 21 | **Cost:** Free tier available for all services |
12 | 22 | |
13 | 23 | --- |
| @@ -16,7 +26,7 @@ This guide walks you through deploying OpenCodeHub to Vercel using: | ||
16 | 26 | |
17 | 27 | 1. [Prerequisites](#prerequisites) |
18 | 28 | 2. [Setup Turso Database](#setup-turso-database) |
19 | -3. [Setup Google Drive Storage](#setup-google-drive-storage) | |
29 | +3. [Setup S3-compatible Storage (R2)](#setup-s3-compatible-storage) | |
20 | 30 | 4. [Setup Upstash Redis](#setup-upstash-redis) |
21 | 31 | 5. [Configure Project](#configure-project) |
22 | 32 | 6. [Deploy to Vercel](#deploy-to-vercel) |
| @@ -27,32 +37,24 @@ This guide walks you through deploying OpenCodeHub to Vercel using: | ||
27 | 37 | |
28 | 38 | ## Prerequisites |
29 | 39 | |
30 | -Before starting, ensure you have: | |
31 | - | |
32 | -- [x] GitHub account | |
33 | -- [x] Vercel account ([vercel.com](https://vercel.com)) | |
34 | -- [x] Turso account ([turso.tech](https://turso.tech)) | |
35 | -- [x] Google Cloud account ([console.cloud.google.com](https://console.cloud.google.com)) | |
36 | -- [x] Upstash account ([upstash.com](https://upstash.com)) | |
37 | -- [x] OpenCodeHub repository forked/cloned | |
40 | +- [ ] GitHub account | |
41 | +- [ ] Vercel account ([vercel.com](https://vercel.com)) | |
42 | +- [ ] Turso account ([turso.tech](https://turso.tech)) | |
43 | +- [ ] Cloudflare account for R2 ([cloudflare.com](https://dash.cloudflare.com)) — or any S3-compatible store | |
44 | +- [ ] Upstash account ([upstash.com](https://upstash.com)) | |
45 | +- [ ] OpenCodeHub repository forked/cloned | |
38 | 46 | |
39 | 47 | --- |
40 | 48 | |
41 | 49 | ## 1. Setup Turso Database |
42 | 50 | |
43 | -Turso provides edge-compatible, serverless SQLite databases perfect for Vercel. | |
51 | +Turso provides edge-compatible, serverless SQLite databases. | |
44 | 52 | |
45 | 53 | ### 1.1. Install Turso CLI |
46 | 54 | |
47 | 55 | ```bash |
48 | 56 | # macOS/Linux |
49 | 57 | curl -sSfL https://get.tur.so/install.sh | bash |
50 | - | |
51 | -# Windows (PowerShell) | |
52 | -irm get.tur.so/install.ps1 | iex | |
53 | - | |
54 | -# Verify installation | |
55 | -turso --version | |
56 | 58 | ``` |
57 | 59 | |
58 | 60 | ### 1.2. Login to Turso |
| @@ -61,8 +63,6 @@ turso --version | ||
61 | 63 | turso auth login |
62 | 64 | ``` |
63 | 65 | |
64 | -This opens your browser to authenticate. | |
65 | - | |
66 | 66 | ### 1.3. Create Database |
67 | 67 | |
68 | 68 | ```bash |
| @@ -73,296 +73,120 @@ turso db create opencodehub | ||
73 | 73 | turso db tokens create opencodehub |
74 | 74 | |
75 | 75 | # Get database URL |
76 | -turso db show opencodehub --url | |
76 | +turso db show opencodehub | |
77 | 77 | ``` |
78 | 78 | |
79 | -**Save these values:** | |
80 | -```env | |
81 | -TURSO_DATABASE_URL=libsql://opencodehub-[your-org].turso.io | |
82 | -TURSO_AUTH_TOKEN=eyJhbGciOiJ... | |
83 | -``` | |
79 | +Save the **database URL** (starts with `libsql://`) and the **auth token** — | |
80 | +you will need them for Vercel. | |
84 | 81 | |
85 | 82 | ### 1.4. Initialize Database Schema |
86 | 83 | |
87 | -```bash | |
88 | -# Download schema | |
89 | -curl -O https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/drizzle/schema.sql | |
90 | - | |
91 | -# Apply schema | |
92 | -turso db shell opencodehub < schema.sql | |
84 | +The app applies schema migrations at startup via `scripts/migrate.ts` | |
85 | +using the committed `drizzle/` migrations: | |
93 | 86 | |
94 | -# Verify tables | |
95 | -turso db shell opencodehub "SELECT name FROM sqlite_master WHERE type='table';" | |
87 | +```bash | |
88 | +DATABASE_DRIVER=turso DATABASE_URL=libsql://... DATABASE_AUTH_TOKEN=... bun run migrate | |
96 | 89 | ``` |
97 | 90 | |
98 | -**Expected output:** | |
99 | -``` | |
100 | -users | |
101 | -repositories | |
102 | -pull_requests | |
103 | -pr_stacks | |
104 | -merge_queue_items | |
105 | -ai_reviews | |
106 | -... | |
107 | -``` | |
91 | +On Vercel, run this once from your machine against the Turso database | |
92 | +before the first deploy (Serverless functions have a short lifespan and | |
93 | +should not run migrations per-request). | |
108 | 94 | |
109 | 95 | --- |
110 | 96 | |
111 | -## 2. Setup Google Drive Storage | |
112 | - | |
113 | -Google Drive API will store Git repositories and LFS files. | |
114 | - | |
115 | -### 2.1. Create Google Cloud Project | |
116 | - | |
117 | -1. Go to [Google Cloud Console](https://console.cloud.google.com) | |
118 | -2. Click **Select a project** → **New Project** | |
119 | -3. Name: `opencodehub-storage` | |
120 | -4. Click **Create** | |
121 | - | |
122 | -### 2.2. Enable Google Drive API | |
123 | - | |
124 | -1. In the Cloud Console, go to **APIs & Services** → **Enable APIs** | |
125 | -2. Search for "Google Drive API" | |
126 | -3. Click **Enable** | |
97 | +## 2. Setup S3-compatible Storage | |
127 | 98 | |
128 | -### 2.3. Create Service Account | |
99 | +Use an S3-compatible object store for git objects, LFS, and artifacts. | |
100 | +Cloudflare R2 is recommended for Serverless because egress is free. | |
129 | 101 | |
130 | -1. Go to **APIs & Services** → **Credentials** | |
131 | -2. Click **Create Credentials** → **Service Account** | |
132 | -3. Name: `opencodehub-storage-sa` | |
133 | -4. Click **Create and Continue** | |
134 | -5. Grant role: **Editor** | |
135 | -6. Click **Done** | |
102 | +### 2.1. Create an R2 Bucket | |
136 | 103 | |
137 | -### 2.4. Generate Service Account Key | |
104 | +1. Cloudflare Dashboard → **R2** → **Create bucket** → name it `opencodehub` | |
105 | +2. Go to **R2 → Manage R2 API Tokens** → **Create API Token** | |
106 | + - Permissions: **Object Read & Write** on the bucket | |
107 | +3. Save the **Access Key ID** and **Secret Access Key** | |
138 | 108 | |
139 | -1. Click on the service account you just created | |
140 | -2. Go to **Keys** tab | |
141 | -3. Click **Add Key** → **Create new key** | |
142 | -4. Choose **JSON** | |
143 | -5. Click **Create** (downloads `opencodehub-storage-xxxxx.json`) | |
109 | +Your endpoint is: | |
110 | +`https://<ACCOUNT_ID>.r2.cloudflarestorage.com` | |
111 | +(found in **R2 → Overview → Account ID**). | |
144 | 112 | |
145 | -### 2.5. Create Google Drive Folder | |
146 | - | |
147 | -1. Go to [Google Drive](https://drive.google.com) | |
148 | -2. Create a new folder: `OpenCodeHub-Repositories` | |
149 | -3. Right-click → **Share** | |
150 | -4. Add the service account email: `opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com` | |
151 | -5. Grant **Editor** access | |
152 | -6. Copy the **Folder ID** from the URL: | |
153 | - ``` | |
154 | - https://drive.google.com/drive/folders/1a2B3c4D5e6F7g8H9i0J | |
155 | - ^^^^^^^^^^^^^^^^^^^^ | |
156 | - This is your folder ID | |
157 | - ``` | |
158 | - | |
159 | -### 2.6. Prepare Service Account Credentials | |
160 | - | |
161 | -Open the downloaded JSON file and extract these values: | |
162 | - | |
163 | -```json | |
164 | -{ | |
165 | - "type": "service_account", | |
166 | - "project_id": "opencodehub-storage", | |
167 | - "private_key_id": "abc123...", | |
168 | - "private_key": "-----BEGIN PRIVATE KEY-----\n...", | |
169 | - "client_email": "opencodehub-storage-sa@...", | |
170 | - "client_id": "123456789...", | |
171 | - ... | |
172 | -} | |
173 | -``` | |
174 | - | |
175 | -**Save for later:** | |
176 | -```env | |
177 | -GOOGLE_DRIVE_FOLDER_ID=1a2B3c4D5e6F7g8H9i0J | |
178 | -GOOGLE_SERVICE_ACCOUNT_EMAIL=opencodehub-storage-sa@opencodehub-storage.iam.gserviceaccount.com | |
179 | -GOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n... | |
180 | -GOOGLE_CLIENT_ID=123456789... | |
181 | -``` | |
113 | +> Any S3-compatible provider works: AWS S3 (`STORAGE_ENDPOINT` empty), | |
114 | +> MinIO (`http://minio:9000`), Garage, SeaweedFS, Ceph RGW, Wasabi, B2. | |
182 | 115 | |
183 | 116 | --- |
184 | 117 | |
185 | 118 | ## 3. Setup Upstash Redis |
186 | 119 | |
187 | -Upstash provides serverless Redis, perfect for Vercel edge functions. | |
120 | +Upstash provides serverless Redis with a REST API — ideal for Vercel. | |
188 | 121 | |
189 | 122 | ### 3.1. Create Redis Database |
190 | 123 | |
191 | 124 | 1. Go to [Upstash Console](https://console.upstash.com) |
192 | -2. Click **Create Database** | |
193 | -3. **Name:** `opencodehub-sessions` | |
194 | -4. **Region:** Choose closest to your users (e.g., `us-east-1`) | |
195 | -5. **Type:** Regional (free tier) | |
196 | -6. Click **Create** | |
125 | +2. Click **Create Database** → name it `opencodehub` | |
126 | +3. Region: **Global** (or closest to your Vercel region) | |
197 | 127 | |
198 | 128 | ### 3.2. Get Connection Details |
199 | 129 | |
200 | -After creation, copy these values: | |
201 | - | |
202 | -**REST API:** | |
203 | -```env | |
204 | -UPSTASH_REDIS_REST_URL=https://us1-prepared-moose-12345.upstash.io | |
205 | -UPSTASH_REDIS_REST_TOKEN=AaBbCc...== | |
206 | -``` | |
207 | - | |
208 | -**Redis URL (optional):** | |
209 | -```env | |
210 | -REDIS_URL=redis://default:[email protected]:port | |
211 | -``` | |
130 | +In the database details page: | |
212 | 131 | |
213 | -### 3.3. Configure Redis for Sessions | |
132 | +- **REST URL** — e.g. `https://your-redis.upstash.io` | |
133 | +- **REST Token** — long alphanumeric token | |
214 | 134 | |
215 | -Upstash Redis will handle: | |
216 | -- User sessions | |
217 | -- Rate limiting | |
218 | -- Temporary locks | |
219 | -- Cache | |
220 | - | |
221 | -No additional setup needed! | |
135 | +Save both. | |
222 | 136 | |
223 | 137 | --- |
224 | 138 | |
225 | 139 | ## 4. Configure Project |
226 | 140 | |
227 | -### 4.1. Update Database Configuration | |
228 | - | |
229 | -Create/update `src/db/turso.ts`: | |
141 | +No code changes are required — everything is configured via environment | |
142 | +variables that the built-in factory reads: | |
230 | 143 | |
231 | -```typescript | |
232 | -import { drizzle } from 'drizzle-orm/libsql'; | |
233 | -import { createClient } from '@libsql/client'; | |
144 | +- Database: `src/db/index.ts` (reads `DATABASE_DRIVER` + `DATABASE_URL`) | |
145 | +- Storage: `src/lib/storage.ts` (reads `STORAGE_TYPE` + `STORAGE_*`) | |
146 | +- Redis: reads `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` | |
147 | + (or `REDIS_URL`) | |
234 | 148 | |
235 | -const turso = createClient({ | |
236 | - url: process.env.TURSO_DATABASE_URL!, | |
237 | - authToken: process.env.TURSO_AUTH_TOKEN!, | |
238 | -}); | |
239 | - | |
240 | -export const db = drizzle(turso); | |
241 | -``` | |
242 | - | |
243 | -### 4.2. Update Storage Configuration | |
244 | - | |
245 | -Create/update `src/lib/storage/google-drive.ts`: | |
246 | - | |
247 | -```typescript | |
248 | -import { google } from 'googleapis'; | |
249 | - | |
250 | -const auth = new google.auth.GoogleAuth({ | |
251 | - credentials: { | |
252 | - client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL, | |
253 | - private_key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'), | |
254 | - }, | |
255 | - scopes: ['https://www.googleapis.com/auth/drive.file'], | |
256 | -}); | |
257 | - | |
258 | -const drive = google.drive({ version: 'v3', auth }); | |
259 | -const folderId = process.env.GOOGLE_DRIVE_FOLDER_ID; | |
260 | - | |
261 | -export async function uploadFile(fileName: string, content: Buffer) { | |
262 | - const response = await drive.files.create({ | |
263 | - requestBody: { | |
264 | - name: fileName, | |
265 | - parents: [folderId!], | |
266 | - }, | |
267 | - media: { | |
268 | - body: content, | |
269 | - }, | |
270 | - }); | |
271 | - | |
272 | - return response.data.id; | |
273 | -} | |
274 | - | |
275 | -export async function downloadFile(fileId: string) { | |
276 | - const response = await drive.files.get( | |
277 | - { fileId, alt: 'media' }, | |
278 | - { responseType: 'arraybuffer' } | |
279 | - ); | |
280 | - | |
281 | - return Buffer.from(response.data as ArrayBuffer); | |
282 | -} | |
283 | - | |
284 | -export async function deleteFile(fileId: string) { | |
285 | - await drive.files.delete({ fileId }); | |
286 | -} | |
287 | -``` | |
288 | - | |
289 | -### 4.3. Update Redis Configuration | |
290 | - | |
291 | -Create/update `src/lib/redis.ts`: | |
292 | - | |
293 | -```typescript | |
294 | -import { Redis } from '@upstash/redis'; | |
295 | - | |
296 | -export const redis = new Redis({ | |
297 | - url: process.env.UPSTASH_REDIS_REST_URL!, | |
298 | - token: process.env.UPSTASH_REDIS_REST_TOKEN!, | |
299 | -}); | |
300 | - | |
301 | -// Session management | |
302 | -export async function setSession(sessionId: string, data: any, ttl = 7 * 24 * 60 * 60) { | |
303 | - await redis.setex(sessionId, ttl, JSON.stringify(data)); | |
304 | -} | |
305 | - | |
306 | -export async function getSession(sessionId: string) { | |
307 | - const data = await redis.get(sessionId); | |
308 | - return data ? JSON.parse(data as string) : null; | |
309 | -} | |
310 | - | |
311 | -export async function deleteSession(sessionId: string) { | |
312 | - await redis.del(sessionId); | |
313 | -} | |
314 | -``` | |
315 | - | |
316 | -### 4.4. Install Dependencies | |
317 | - | |
318 | -```bash | |
319 | -# Add Turso client | |
320 | -bun add @libsql/client drizzle-orm | |
321 | - | |
322 | -# Add Google Drive API | |
323 | -bun add googleapis | |
324 | - | |
325 | -# Add Upstash Redis | |
326 | -bun add @upstash/redis | |
327 | -``` | |
328 | - | |
329 | -### 4.5. Update Environment Variables Template | |
330 | - | |
331 | -Create `.env.example`: | |
149 | +### 4.1. Environment Variables Template | |
332 | 150 | |
333 | 151 | ```env |
334 | -# === Turso Database === | |
335 | -TURSO_DATABASE_URL=libsql://your-db.turso.io | |
336 | -TURSO_AUTH_TOKEN=your-token | |
337 | - | |
338 | -# === Google Drive Storage === | |
339 | -GOOGLE_DRIVE_FOLDER_ID=your-folder-id | |
340 | -GOOGLE_SERVICE_ACCOUNT_EMAIL=your-sa@project.iam.gserviceaccount.com | |
341 | -GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" | |
342 | -GOOGLE_CLIENT_ID=your-client-id | |
343 | - | |
344 | -# === Upstash Redis === | |
152 | +# === Database (Turso) === | |
153 | +DATABASE_DRIVER=turso | |
154 | +DATABASE_URL=libsql://your-db.turso.io | |
155 | +DATABASE_AUTH_TOKEN=your-token | |
156 | + | |
157 | +# === Storage (S3-compatible — R2 shown) === | |
158 | +STORAGE_TYPE=s3 | |
159 | +STORAGE_BUCKET=opencodehub | |
160 | +STORAGE_REGION=auto | |
161 | +STORAGE_ENDPOINT=https://<ACCOUNT_ID>.r2.cloudflarestorage.com | |
162 | +STORAGE_ACCESS_KEY_ID=your-access-key-id | |
163 | +STORAGE_SECRET_ACCESS_KEY=your-secret-access-key | |
164 | + | |
165 | +# === Redis (Upstash) === | |
345 | 166 | UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io |
346 | 167 | UPSTASH_REDIS_REST_TOKEN=your-token |
347 | 168 | |
348 | 169 | # === Application === |
349 | 170 | SITE_URL=https://your-app.vercel.app |
171 | +NODE_ENV=production | |
350 | 172 | JWT_SECRET=generate-a-strong-secret |
351 | 173 | SESSION_SECRET=generate-another-strong-secret |
352 | 174 | INTERNAL_HOOK_SECRET=generate-hook-secret |
175 | +CRON_SECRET=generate-cron-secret | |
176 | +RUNNER_SECRET=generate-runner-secret | |
177 | +AI_CONFIG_ENCRYPTION_KEY=generate-encryption-key | |
178 | +WORKFLOW_SECRET_ENCRYPTION_KEY=generate-encryption-key | |
179 | +METRICS_TOKEN=generate-metrics-token | |
353 | 180 | |
354 | 181 | # === Optional: AI Review === |
355 | 182 | AI_PROVIDER=openai |
356 | 183 | OPENAI_API_KEY=sk-... |
357 | 184 | ``` |
358 | 185 | |
359 | -### 4.6. Generate Secrets | |
186 | +### 4.2. Generate Secrets | |
360 | 187 | |
361 | 188 | ```bash |
362 | -# Generate secrets | |
363 | -openssl rand -base64 32 # JWT_SECRET | |
364 | -openssl rand -base64 32 # SESSION_SECRET | |
365 | -openssl rand -base64 32 # INTERNAL_HOOK_SECRET | |
189 | +openssl rand -base64 32 # each secret | |
366 | 190 | ``` |
367 | 191 | |
368 | 192 | --- |
| @@ -382,163 +206,61 @@ vercel login | ||
382 | 206 | |
383 | 207 | 1. Go to [Vercel Dashboard](https://vercel.com/dashboard) |
384 | 208 | 2. Click **Add New** → **Project** |
385 | -3. Import your GitHub repository | |
386 | -4. Select `OpencodeHub` | |
209 | +3. Import your OpenCodeHub repository | |
387 | 210 | |
388 | 211 | **Option B: Via CLI** |
389 | 212 | |
390 | 213 | ```bash |
391 | -cd opencodehub | |
392 | -vercel | |
214 | +vercel --prod | |
393 | 215 | ``` |
394 | 216 | |
395 | 217 | ### 5.3. Configure Environment Variables |
396 | 218 | |
397 | -In Vercel Dashboard: | |
398 | - | |
399 | -1. Go to **Project Settings** → **Environment Variables** | |
400 | -2. Add all variables from step 4.5: | |
401 | - | |
402 | -**Turso:** | |
403 | -- `TURSO_DATABASE_URL` = `libsql://opencodehub-xxxx.turso.io` | |
404 | -- `TURSO_AUTH_TOKEN` = `eyJhbG...` | |
405 | - | |
406 | -**Google Drive:** | |
407 | -- `GOOGLE_DRIVE_FOLDER_ID` = `1a2B3c4D5e6F7g8H9i0J` | |
408 | -- `GOOGLE_SERVICE_ACCOUNT_EMAIL` = `opencodehub-storage-sa@...` | |
409 | -- `GOOGLE_PRIVATE_KEY` = Paste entire private key (including `\n`) | |
410 | -- `GOOGLE_CLIENT_ID` = `123456789...` | |
411 | - | |
412 | -**Upstash Redis:** | |
413 | -- `UPSTASH_REDIS_REST_URL` = `https://...upstash.io` | |
414 | -- `UPSTASH_REDIS_REST_TOKEN` = `AaBbCc...` | |
415 | - | |
416 | -**Application:** | |
417 | -- `SITE_URL` = `https://your-project.vercel.app` (update later) | |
418 | -- `JWT_SECRET` = Generated secret | |
419 | -- `SESSION_SECRET` = Generated secret | |
420 | -- `INTERNAL_HOOK_SECRET` = Generated secret | |
421 | - | |
422 | -**AI (Optional):** | |
423 | -- `AI_PROVIDER` = `openai` | |
424 | -- `OPENAI_API_KEY` = `sk-...` | |
219 | +In **Vercel → Project → Settings → Environment Variables**, add every | |
220 | +variable from section 4.1. | |
425 | 221 | |
426 | 222 | ### 5.4. Configure Build Settings |
427 | 223 | |
428 | -In **Project Settings** → **Build & Development Settings**: | |
224 | +Use the defaults (Vercel auto-detects Astro): | |
429 | 225 | |
430 | -- **Framework Preset:** Astro | |
431 | -- **Build Command:** `bun run build` | |
432 | -- **Output Directory:** `dist` | |
433 | -- **Install Command:** `bun install` | |
226 | +- **Build Command:** `npm run build` | |
227 | +- **Output Directory:** `dist/` | |
228 | +- **Framework:** Astro | |
434 | 229 | |
435 | -### 5.5. Deploy! | |
230 | +> The repo ships `@astrojs/vercel` and a `vercel.json`. For edge use, | |
231 | +> switch the adapter in `astro.config.mjs` from `@astrojs/node` to | |
232 | +> `@astrojs/vercel` with `output: "server"` (Turso + Upstash are both | |
233 | +> edge-compatible). | |
436 | 234 | |
437 | -Click **Deploy** or run: | |
235 | +### 5.5. Deploy! | |
438 | 236 | |
439 | 237 | ```bash |
440 | 238 | vercel --prod |
441 | 239 | ``` |
442 | 240 | |
443 | -**Deployment will:** | |
444 | -1. Install dependencies | |
445 | -2. Build the project | |
446 | -3. Deploy to Vercel Edge Network | |
447 | -4. Provide deployment URL | |
448 | - | |
449 | 241 | --- |
450 | 242 | |
451 | 243 | ## 6. Post-Deployment |
452 | 244 | |
453 | 245 | ### 6.1. Update SITE_URL |
454 | 246 | |
455 | -1. Copy your Vercel deployment URL: `https://opencodehub-abc123.vercel.app` | |
456 | -2. Update environment variable: | |
457 | - - Go to **Settings** → **Environment Variables** | |
458 | - - Edit `SITE_URL` = your actual URL | |
459 | -3. **Redeploy** (Vercel Dashboard → Deployments → Redeploy) | |
247 | +Set `SITE_URL` to your final domain, e.g. `https://git.yourdomain.com`. | |
460 | 248 | |
461 | 249 | ### 6.2. Create Admin User |
462 | 250 | |
463 | -**Option A: Via Turso CLI** | |
464 | - | |
465 | -```bash | |
466 | -turso db shell opencodehub | |
467 | - | |
468 | -INSERT INTO users (id, email, password_hash, username, role, created_at) | |
469 | -VALUES ( | |
470 | - 'admin-user-id', | |
471 | - '[email protected]', | |
472 | - '$2b$10$...', -- bcrypt hash of your password | |
473 | - 'admin', | |
474 | - 'admin', | |
475 | - current_timestamp | |
476 | -); | |
477 | -``` | |
478 | - | |
479 | -**Generate bcrypt hash:** | |
480 | -```javascript | |
481 | -// Run in Node.js | |
482 | -const bcrypt = require('bcrypt'); | |
483 | -const hash = await bcrypt.hash('your-password', 10); | |
484 | -console.log(hash); | |
485 | -``` | |
486 | - | |
487 | -**Option B: Via Deployment Script** | |
488 | - | |
489 | -Create `scripts/create-admin-vercel.ts`: | |
490 | - | |
491 | -```typescript | |
492 | -import { db } from '../src/db/turso'; | |
493 | -import { users } from '../src/db/schema'; | |
494 | -import bcrypt from 'bcrypt'; | |
495 | - | |
496 | -const email = process.env.ADMIN_EMAIL || '[email protected]'; | |
497 | -const password = process.env.ADMIN_PASSWORD || 'changeme'; | |
498 | - | |
499 | -const hash = await bcrypt.hash(password, 10); | |
500 | - | |
501 | -await db.insert(users).values({ | |
502 | - id: crypto.randomUUID(), | |
503 | - email, | |
504 | - username: 'admin', | |
505 | - password_hash: hash, | |
506 | - role: 'admin', | |
507 | - createdAt: new Date(), | |
508 | -}); | |
509 | - | |
510 | -console.log(`✅ Admin user created: ${email}`); | |
511 | -``` | |
512 | - | |
513 | -Run once: | |
514 | 251 | ```bash |
515 | -[email protected] ADMIN_PASSWORD=strong123 bun scripts/create-admin-vercel.ts | |
252 | +# From your machine, once, against the same Turso database: | |
253 | +DATABASE_DRIVER=turso DATABASE_URL=libsql://... DATABASE_AUTH_TOKEN=... bun run scripts/seed-admin.ts | |
516 | 254 | ``` |
517 | 255 | |
518 | 256 | ### 6.3. Configure Custom Domain (Optional) |
519 | 257 | |
520 | -1. Go to **Settings** → **Domains** | |
521 | -2. Add your custom domain: `git.yourcompany.com` | |
522 | -3. Follow DNS configuration instructions | |
523 | -4. Update `SITE_URL` to your custom domain | |
258 | +In Vercel → Project → **Settings → Domains**, add your domain. | |
524 | 259 | |
525 | 260 | ### 6.4. Setup Monitoring |
526 | 261 | |
527 | -**Vercel Analytics:** | |
528 | -1. Go to **Analytics** tab | |
529 | -2. Enable **Web Analytics** | |
530 | -3. Enable **Speed Insights** | |
531 | - | |
532 | -**Sentry (Optional):** | |
533 | -```bash | |
534 | -bun add @sentry/astro | |
535 | -``` | |
536 | - | |
537 | -Add to `.env`: | |
538 | -```env | |
539 | -SENTRY_DSN=https://[email protected]/... | |
540 | -``` | |
262 | +- Prometheus: scrape `GET /api/metrics` with `Authorization: Bearer $METRICS_TOKEN` | |
263 | +- Use Vercel's built-in analytics + logs for request/error tracking | |
541 | 264 | |
542 | 265 | --- |
543 | 266 | |
| @@ -546,249 +268,68 @@ SENTRY_DSN=https://[email protected]/... | ||
546 | 268 | |
547 | 269 | ### 7.1. Test Database Connection |
548 | 270 | |
549 | -Visit: `https://your-app.vercel.app/api/health` | |
550 | - | |
551 | -Should return: | |
552 | -```json | |
553 | -{ | |
554 | - "status": "ok", | |
555 | - "database": "connected", | |
556 | - "redis": "connected", | |
557 | - "storage": "configured" | |
558 | -} | |
271 | +```bash | |
272 | +turso db shell opencodehub | |
273 | +# Run: SELECT count(*) FROM users; | |
559 | 274 | ``` |
560 | 275 | |
561 | 276 | ### 7.2. Test Storage |
562 | 277 | |
563 | -1. Login as admin | |
564 | -2. Create a repository | |
565 | -3. Push a commit | |
566 | -4. Verify file appears in Google Drive folder | |
278 | +1. Create a repository with a large file (LFS) | |
279 | +2. Push — verify the object appears in the R2 bucket | |
567 | 280 | |
568 | 281 | ### 7.3. Test Redis |
569 | 282 | |
570 | -1. Login | |
571 | -2. Check session persists | |
572 | -3. Logout | |
573 | -4. Session cleared | |
283 | +Push a git update and confirm webhooks/queues process (Redis-backed). | |
574 | 284 | |
575 | 285 | ### 7.4. Load Test |
576 | 286 | |
577 | 287 | ```bash |
578 | 288 | # Install hey |
579 | -go install github.com/rakyll/hey@latest | |
289 | +brew install hey # or: go install github.com/rakyll/hey@latest | |
580 | 290 | |
581 | 291 | # Test |
582 | -hey -n 1000 -c 10 https://your-app.vercel.app | |
292 | +hey -n 1000 -c 50 https://your-app.vercel.app/api/health | |
583 | 293 | ``` |
584 | 294 | |
585 | -Expected: >90% success rate | |
586 | - | |
587 | 295 | --- |
588 | 296 | |
589 | 297 | ## 8. Troubleshooting |
590 | 298 | |
591 | 299 | ### Database Connection Fails |
592 | 300 | |
593 | -**Error:** `Failed to connect to Turso` | |
594 | - | |
595 | -**Solution:** | |
596 | -```bash | |
597 | -# Verify token is valid | |
598 | -turso db tokens validate opencodehub YOUR_TOKEN | |
599 | - | |
600 | -# Regenerate if needed | |
601 | -turso db tokens create opencodehub | |
602 | -``` | |
603 | - | |
604 | -Update environment variable and redeploy. | |
301 | +- Verify `DATABASE_DRIVER=turso` and the `libsql://` URL + token are correct | |
302 | +- Run `bun run migrate` locally against the same DB (see section 1.4) | |
605 | 303 | |
606 | -### Google Drive Upload Fails | |
304 | +### Storage Upload Fails | |
607 | 305 | |
608 | -**Error:** `Insufficient permissions` | |
609 | - | |
610 | -**Solution:** | |
611 | -1. Verify service account has Editor access to folder | |
612 | -2. Check private key doesn't have extra spaces/newlines | |
613 | -3. Test locally: | |
614 | - ```bash | |
615 | - bun test src/lib/storage/google-drive.test.ts | |
616 | - ``` | |
306 | +- Verify bucket name, endpoint, and access key (S3 v4 signature) | |
307 | +- R2: token must have **Object Read & Write** scope on the bucket | |
617 | 308 | |
618 | 309 | ### Redis Connection Timeout |
619 | 310 | |
620 | -**Error:** `ETIMEDOUT connecting to Redis` | |
621 | - | |
622 | -**Solution:** | |
623 | -1. Verify Upstash Redis region matches Vercel deployment region | |
624 | -2. Check REST URL and token are correct | |
625 | -3. Test connection: | |
626 | - ```bash | |
627 | - curl https://YOUR_REDIS_URL/get/test \ | |
628 | - -H "Authorization: Bearer YOUR_TOKEN" | |
629 | - ``` | |
311 | +- Confirm the Upstash REST URL/token are set (not `REDIS_URL`) | |
630 | 312 | |
631 | 313 | ### Build Fails on Vercel |
632 | 314 | |
633 | -**Error:** `Command "build" exited with 1` | |
634 | - | |
635 | -**Solution:** | |
636 | 315 | ```bash |
637 | 316 | # Test build locally |
638 | -bun run build | |
317 | +npm run build | |
639 | 318 | |
640 | 319 | # Check for TypeScript errors |
641 | -bun run typecheck | |
320 | +npm run typecheck | |
642 | 321 | |
643 | 322 | # Check environment variables are set |
644 | -vercel env ls | |
645 | -``` | |
646 | - | |
647 | -### Out of Memory | |
648 | - | |
649 | -**Error:** `JavaScript heap out of memory` | |
650 | - | |
651 | -** Solution:** | |
652 | -Upgrade Vercel plan or optimize build: | |
653 | - | |
654 | -```json | |
655 | -// package.json | |
656 | -{ | |
657 | - "scripts": { | |
658 | - "build": "NODE_OPTIONS=--max_old_space_size=4096 astro build" | |
659 | - } | |
660 | -} | |
323 | +bun run src/lib/env-validation.ts | |
661 | 324 | ``` |
662 | 325 | |
663 | 326 | --- |
664 | 327 | |
665 | -## Cost Estimation | |
666 | - | |
667 | -### Free Tier Limits | |
668 | - | |
669 | -**Turso:** | |
670 | -- Database: Free up to 8 GB | |
671 | -- Rows: 500M | |
672 | -- Locations: 1 primary + 2 replicas | |
673 | - | |
674 | -**Upstash Redis:** | |
675 | -- Commands: 10,000/day | |
676 | -- Storage: 256 MB | |
677 | -- Bandwidth: 256 MB/month | |
678 | - | |
679 | -**Google Drive:** | |
680 | -- Storage: 15 GB free | |
681 | -- API calls: Unlimited | |
682 | - | |
683 | -**Vercel:** | |
684 | -- Bandwidth: 100 GB/month | |
685 | -- Deployments: Unlimited | |
686 | -- Edge Functions: 100 GB-hrs | |
687 | - | |
688 | -### Estimated Monthly Cost | |
689 | - | |
690 | -**Low traffic (< 1,000 users):** | |
691 | -- All free tier ✅ | |
692 | - | |
693 | -**Medium traffic (1,000-10,000 users):** | |
694 | -- Turso: $0 (within limits) | |
695 | -- Upstash: $10/month (higher tier) | |
696 | -- Google Drive: $2/month (Google One 100GB) | |
697 | -- Vercel: $20/month (Pro plan) | |
698 | -**Total: ~$32/month** | |
699 | - | |
700 | -**High traffic (>10,000 users):** | |
701 | -- Turso: $29/month (Pro) | |
702 | -- Upstash: $50/month (Pro) | |
703 | -- Google Drive: $10/month (Google Workspace) | |
704 | -- Vercel: $20/month (Pro) | |
705 | -**Total: ~$109/month** | |
706 | - | |
707 | - | |
708 | -## Performance Optimization | |
709 | - | |
710 | -### Edge Caching | |
711 | - | |
712 | -Add to `vercel.json`: | |
713 | - | |
714 | -```json | |
715 | -{ | |
716 | - "headers": [ | |
717 | - { | |
718 | - "source": "/assets/(.*)", | |
719 | - "headers": [ | |
720 | - { | |
721 | - "key": "Cache-Control", | |
722 | - "value": "public, max-age=31536000, immutable" | |
723 | - } | |
724 | - ] | |
725 | - } | |
726 | - ] | |
727 | -} | |
728 | -``` | |
729 | - | |
730 | -### Database Connection Pooling | |
731 | - | |
732 | -Turso handles this automatically with libSQL. | |
733 | - | |
734 | -### Redis Caching Strategy | |
735 | - | |
736 | -```typescript | |
737 | -// Cache expensive queries | |
738 | -async function getRepository(id: string) { | |
739 | - const cached = await redis.get(`repo:${id}`); | |
740 | - if (cached) return JSON.parse(cached as string); | |
741 | - | |
742 | - const repo = await db.query.repositories.findFirst({ where: eq(repositories.id, id) }); | |
743 | - await redis.setex(`repo:${id}`, 300, JSON.stringify(repo)); // Cache 5 min | |
744 | - | |
745 | - return repo; | |
746 | -} | |
747 | -``` | |
748 | - | |
749 | - | |
750 | -## Security Checklist | |
751 | - | |
752 | -- [ ] All secrets in environment variables (not committed to Git) | |
753 | -- [ ] HTTPS enabled (automatic on Vercel) | |
754 | -- [ ] Database access restricted to Vercel IPs | |
755 | -- [ ] Google Drive folder shared only with service account | |
756 | -- [ ] Redis requires authentication | |
757 | -- [ ] Rate limiting enabled | |
758 | -- [ ] CORS configured properly | |
759 | -- [ ] CSP headers set | |
760 | - | |
761 | - | |
762 | -## Next Steps | |
763 | - | |
764 | -1. **Setup CI/CD**: Automatic deployments on push to main | |
765 | -2. **Add Monitoring**: Set up Sentry for error tracking | |
766 | -3. **Configure Backups**: Turso auto-backups + manual exports | |
767 | -4. **Scale Redis**: Upgrade to Global for multi-region | |
768 | -5. **Add CDN**: Use Vercel Edge Network or Cloudflare | |
769 | - | |
770 | - | |
771 | -## Additional Resources | |
772 | - | |
773 | -- [Turso Documentation](https://docs.turso.tech) | |
774 | -- [Google Drive API Reference](https://developers.google.com/drive/api/v3/reference) | |
775 | -- [Upstash Redis Docs](https://docs.upstash.com/redis) | |
776 | -- [Vercel Deployment Docs](https://vercel.com/docs) | |
777 | -- [OpenCodeHub GitHub](https://github.com/swadhinbiswas/OpencodeHub) | |
778 | - | |
779 | - | |
780 | -## Support | |
781 | - | |
782 | -**Need help?** | |
783 | -- 💬 [Discord Community](https://discord.gg/opencodehub) | |
784 | -- 🐛 [GitHub Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) | |
785 | -- 📧 [Email Support](mailto:[email protected]) | |
786 | - | |
328 | +## Limitations vs. Self-Hosted | |
787 | 329 | |
788 | -**Congratulations!** 🎉 Your OpenCodeHub instance is now running on Vercel with world-class serverless infrastructure! | |
330 | +- **No CI/CD runner** — pipeline execution requires a Docker host; on Vercel | |
331 | + use a self-hosted runner (see `packages/ci-runner` / `Dockerfile.runner`) | |
332 | +- **No SSH Git** — SSH git requires a long-lived process | |
333 | + (`scripts/ssh-server.ts`); use HTTP git with PATs on Serverless | |
334 | +- **Background worker** — scheduled jobs (`scripts/worker.ts`) need a | |
335 | + long-running host; run it in a small container next to your DB | |
789 | 336 | |
▼astro.config.mjs
+16-5
| @@ -1,24 +1,35 @@ | ||
1 | 1 | import node from "@astrojs/node"; |
2 | 2 | import react from "@astrojs/react"; |
3 | -import tailwind from "@astrojs/tailwind"; | |
4 | 3 | import { defineConfig } from "astro/config"; |
4 | +import icon from "astro-icon"; | |
5 | + | |
6 | +import { config } from "dotenv"; | |
7 | +config(); | |
8 | +// Load environment variables safely | |
9 | +const port = parseInt(process.env.PORT || "4321"); | |
5 | 10 | |
6 | 11 | export default defineConfig({ |
7 | 12 | integrations: [ |
8 | 13 | react(), |
9 | - tailwind({ | |
10 | - applyBaseStyles: false, | |
11 | - }), | |
14 | + icon(), | |
12 | 15 | ], |
13 | 16 | output: "server", |
14 | 17 | adapter: node({ |
15 | 18 | mode: "standalone", |
16 | 19 | }), |
17 | 20 | server: { |
18 | - port: parseInt(process.env.PORT || "4321"), | |
21 | + port, | |
19 | 22 | host: true, |
20 | 23 | }, |
21 | 24 | vite: { |
25 | + css: { | |
26 | + transformer: 'postcss', | |
27 | + }, | |
28 | + server: { | |
29 | + watch: { | |
30 | + ignored: ['**/data/**', '**/repos/**', '**/storage/**', '**/.tmp/**', '**/postgres/**', '**/redis/**'], | |
31 | + }, | |
32 | + }, | |
22 | 33 | optimizeDeps: { |
23 | 34 | exclude: ["nodegit", "better-sqlite3"], |
24 | 35 | }, |
25 | 36 | |
▼bun.lock
+212-115
| @@ -8,7 +8,6 @@ | ||
8 | 8 | "@astrojs/check": "^0.9.6", |
9 | 9 | "@astrojs/node": "^8.3.0", |
10 | 10 | "@astrojs/react": "^3.6.0", |
11 | - "@astrojs/tailwind": "^5.1.0", | |
12 | 11 | "@astrojs/vercel": "^7.7.0", |
13 | 12 | "@aws-sdk/client-s3": "^3.948.0", |
14 | 13 | "@aws-sdk/lib-storage": "^3.972.0", |
| @@ -16,7 +15,11 @@ | ||
16 | 15 | "@dnd-kit/core": "^6.3.1", |
17 | 16 | "@dnd-kit/sortable": "^10.0.0", |
18 | 17 | "@dnd-kit/utilities": "^3.2.2", |
18 | + "@google/genai": "^2.11.0", | |
19 | + "@iconify-json/file-icons": "^1.2.2", | |
20 | + "@iconify-json/vscode-icons": "^1.2.67", | |
19 | 21 | "@libsql/client": "^0.15.15", |
22 | + "@mistralai/mistralai": "^2.4.1", | |
20 | 23 | "@monaco-editor/react": "^4.6.0", |
21 | 24 | "@opentelemetry/auto-instrumentations-node": "^0.76.0", |
22 | 25 | "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", |
| @@ -51,6 +54,8 @@ | ||
51 | 54 | "@upstash/redis": "^1.36.0", |
52 | 55 | "arctic": "^3.7.0", |
53 | 56 | "astro": "^4.11.0", |
57 | + "astro-icon": "^1.1.5", | |
58 | + "autoprefixer": "^10.4.19", | |
54 | 59 | "bcryptjs": "^2.4.3", |
55 | 60 | "better-sqlite3": "^11.1.2", |
56 | 61 | "bullmq": "^5.65.1", |
| @@ -60,11 +65,13 @@ | ||
60 | 65 | "cobe": "^0.6.5", |
61 | 66 | "date-fns": "^4.1.0", |
62 | 67 | "dockerode": "^4.0.12", |
68 | + "dotenv": "^17.4.2", | |
63 | 69 | "drizzle-orm": "^0.45.2", |
64 | 70 | "framer-motion": "^11.18.2", |
65 | 71 | "github-markdown-css": "^5.8.1", |
66 | 72 | "graphql": "^16.12.0", |
67 | 73 | "graphql-yoga": "^5.18.0", |
74 | + "highlight.js": "^11.11.1", | |
68 | 75 | "ioredis": "^5.9.2", |
69 | 76 | "isomorphic-dompurify": "^2.35.0", |
70 | 77 | "isomorphic-git": "^1.25.10", |
| @@ -86,6 +93,7 @@ | ||
86 | 93 | "pg": "^8.16.3", |
87 | 94 | "pino": "^10.1.0", |
88 | 95 | "pino-loki": "^3.0.0", |
96 | + "postcss": "^8.5.2", | |
89 | 97 | "postgres": "^3.4.7", |
90 | 98 | "prom-client": "^15.1.3", |
91 | 99 | "qrcode": "^1.5.4", |
| @@ -93,6 +101,7 @@ | ||
93 | 101 | "react-dom": "^18.3.1", |
94 | 102 | "react-globe.gl": "^2.37.0", |
95 | 103 | "recharts": "^3.7.0", |
104 | + "rehype-highlight": "^7.0.2", | |
96 | 105 | "rehype-sanitize": "^6.0.0", |
97 | 106 | "rehype-stringify": "^10.0.1", |
98 | 107 | "remark-gfm": "^4.0.1", |
| @@ -103,6 +112,7 @@ | ||
103 | 112 | "sonner": "^2.0.7", |
104 | 113 | "ssh2": "^1.15.0", |
105 | 114 | "tailwind-merge": "^2.6.0", |
115 | + "tailwindcss": "^3.4.4", | |
106 | 116 | "tailwindcss-animate": "^1.0.7", |
107 | 117 | "three": "^0.182.0", |
108 | 118 | "unified": "^11.0.5", |
| @@ -129,12 +139,10 @@ | ||
129 | 139 | "@types/ssh2": "^1.15.0", |
130 | 140 | "@types/three": "^0.182.0", |
131 | 141 | "@vitest/coverage-v8": "^4.1.5", |
132 | - "autoprefixer": "^10.4.19", | |
133 | 142 | "drizzle-kit": "^0.31.8", |
134 | 143 | "pino-pretty": "^13.1.3", |
135 | - "postcss": "^8.5.10", | |
136 | - "tailwindcss": "^3.4.4", | |
137 | 144 | "typescript": "^5.9.3", |
145 | + "vite": "^5.4.11", | |
138 | 146 | "vitest": "^4.1.5", |
139 | 147 | }, |
140 | 148 | }, |
| @@ -170,11 +178,11 @@ | ||
170 | 178 | }, |
171 | 179 | }, |
172 | 180 | "overrides": { |
173 | - "brace-expansion": "^2.1.1", | |
181 | + "brace-expansion": "^2.0.1", | |
174 | 182 | "cross-spawn": "^7.0.6", |
175 | 183 | "devalue": "^5.8.1", |
176 | 184 | "diff": "^5.2.0", |
177 | - "fast-uri": "^3.1.2", | |
185 | + "fast-uri": "^3.0.6", | |
178 | 186 | "fast-xml-builder": "^1.2.0", |
179 | 187 | "fast-xml-parser": "^5.8.0", |
180 | 188 | "lodash": "^4.17.23", |
| @@ -185,15 +193,18 @@ | ||
185 | 193 | "protobufjs": "^7.6.0", |
186 | 194 | "qs": "^6.15.0", |
187 | 195 | "rollup": "^4.61.0", |
188 | - "tar": "^7.5.16", | |
196 | + "tar": "^7.5.8", | |
189 | 197 | "ws": "^8.18.0", |
190 | - "yaml": "^2.8.0", | |
191 | 198 | }, |
192 | 199 | "packages": { |
193 | 200 | "@acemir/cssom": ["@acemir/[email protected]", "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.31.tgz", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="], |
194 | 201 | |
195 | 202 | "@alloc/quick-lru": ["@alloc/[email protected]", "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], |
196 | 203 | |
204 | + "@antfu/install-pkg": ["@antfu/[email protected]", "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=="], | |
205 | + | |
206 | + "@antfu/utils": ["@antfu/[email protected]", "https://registry.npmmirror.com/@antfu/utils/-/utils-8.1.1.tgz", {}, "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ=="], | |
207 | + | |
197 | 208 | "@asamuzakjp/css-color": ["@asamuzakjp/[email protected]", "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=="], |
198 | 209 | |
199 | 210 | "@asamuzakjp/dom-selector": ["@asamuzakjp/[email protected]", "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 @@ | ||
218 | 229 | |
219 | 230 | "@astrojs/react": ["@astrojs/[email protected]", "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=="], |
220 | 231 | |
221 | - "@astrojs/tailwind": ["@astrojs/[email protected]", "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=="], | |
222 | - | |
223 | 232 | "@astrojs/telemetry": ["@astrojs/[email protected]", "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=="], |
224 | 233 | |
225 | 234 | "@astrojs/vercel": ["@astrojs/[email protected]", "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 @@ | ||
468 | 477 | |
469 | 478 | "@floating-ui/utils": ["@floating-ui/[email protected]", "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], |
470 | 479 | |
480 | + "@google/genai": ["@google/[email protected]", "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=="], | |
481 | + | |
471 | 482 | "@graphql-tools/executor": ["@graphql-tools/[email protected]", "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=="], |
472 | 483 | |
473 | 484 | "@graphql-tools/merge": ["@graphql-tools/[email protected]", "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 @@ | ||
488 | 499 | |
489 | 500 | "@grpc/proto-loader": ["@grpc/[email protected]", "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=="], |
490 | 501 | |
502 | + "@iconify-json/file-icons": ["@iconify-json/[email protected]", "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=="], | |
503 | + | |
504 | + "@iconify-json/vscode-icons": ["@iconify-json/[email protected]", "https://registry.npmmirror.com/@iconify-json/vscode-icons/-/vscode-icons-1.2.67.tgz", { "dependencies": { "@iconify/types": "*" } }, "sha512-/fObxEkBtqK2e1qf1IRjuZ4drWknAnzykdT2ngGAKESf35tCcj0Lx3MdsxYBneE5YbgvsUfOl0hbZ59R5s19vw=="], | |
505 | + | |
506 | + "@iconify/tools": ["@iconify/[email protected]", "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=="], | |
507 | + | |
508 | + "@iconify/types": ["@iconify/[email protected]", "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], | |
509 | + | |
510 | + "@iconify/utils": ["@iconify/[email protected]", "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=="], | |
511 | + | |
491 | 512 | "@img/sharp-darwin-arm64": ["@img/[email protected]", "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=="], |
492 | 513 | |
493 | 514 | "@img/sharp-darwin-x64": ["@img/[email protected]", "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 @@ | ||
584 | 605 | |
585 | 606 | "@mapbox/node-pre-gyp": ["@mapbox/[email protected]", "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=="], |
586 | 607 | |
608 | + "@mistralai/mistralai": ["@mistralai/[email protected]", "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=="], | |
609 | + | |
587 | 610 | "@monaco-editor/loader": ["@monaco-editor/[email protected]", "https://registry.npmmirror.com/@monaco-editor/loader/-/loader-1.7.0.tgz", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="], |
588 | 611 | |
589 | 612 | "@monaco-editor/react": ["@monaco-editor/[email protected]", "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 @@ | ||
1166 | 1189 | |
1167 | 1190 | "@types/react-dom": ["@types/[email protected]", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], |
1168 | 1191 | |
1192 | + "@types/retry": ["@types/[email protected]", "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], | |
1193 | + | |
1169 | 1194 | "@types/ssh2": ["@types/[email protected]", "https://registry.npmmirror.com/@types/ssh2/-/ssh2-1.15.5.tgz", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="], |
1170 | 1195 | |
1171 | 1196 | "@types/stats.js": ["@types/[email protected]", "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="], |
| @@ -1188,6 +1213,8 @@ | ||
1188 | 1213 | |
1189 | 1214 | "@types/ws": ["@types/[email protected]", "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], |
1190 | 1215 | |
1216 | + "@types/yauzl": ["@types/[email protected]", "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], | |
1217 | + | |
1191 | 1218 | "@ungap/structured-clone": ["@ungap/[email protected]", "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], |
1192 | 1219 | |
1193 | 1220 | "@upstash/redis": ["@upstash/[email protected]", "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 @@ | ||
1296 | 1323 | |
1297 | 1324 | "astro": ["[email protected]", "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=="], |
1298 | 1325 | |
1326 | + "astro-icon": ["[email protected]", "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=="], | |
1327 | + | |
1299 | 1328 | "async-lock": ["[email protected]", "https://registry.npmmirror.com/async-lock/-/async-lock-1.4.1.tgz", {}, "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="], |
1300 | 1329 | |
1301 | 1330 | "async-sema": ["[email protected]", "https://registry.npmmirror.com/async-sema/-/async-sema-3.1.1.tgz", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="], |
| @@ -1356,6 +1385,8 @@ | ||
1356 | 1385 | |
1357 | 1386 | "bl": ["[email protected]", "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=="], |
1358 | 1387 | |
1388 | + "boolbase": ["[email protected]", "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], | |
1389 | + | |
1359 | 1390 | "bowser": ["[email protected]", "https://registry.npmmirror.com/bowser/-/bowser-2.14.1.tgz", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="], |
1360 | 1391 | |
1361 | 1392 | "boxen": ["[email protected]", "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 @@ | ||
1368 | 1399 | |
1369 | 1400 | "buffer": ["[email protected]", "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=="], |
1370 | 1401 | |
1402 | + "buffer-crc32": ["[email protected]", "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], | |
1403 | + | |
1404 | + "buffer-equal-constant-time": ["[email protected]", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], | |
1405 | + | |
1371 | 1406 | "buffer-from": ["[email protected]", "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], |
1372 | 1407 | |
1373 | 1408 | "buildcheck": ["[email protected]", "https://registry.npmmirror.com/buildcheck/-/buildcheck-0.0.7.tgz", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="], |
| @@ -1408,7 +1443,11 @@ | ||
1408 | 1443 | |
1409 | 1444 | "chardet": ["[email protected]", "https://registry.npmmirror.com/chardet/-/chardet-2.1.1.tgz", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], |
1410 | 1445 | |
1411 | - "chokidar": ["[email protected]", "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=="], | |
1446 | + "cheerio": ["[email protected]", "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=="], | |
1447 | + | |
1448 | + "cheerio-select": ["[email protected]", "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=="], | |
1449 | + | |
1450 | + "chokidar": ["[email protected]", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], | |
1412 | 1451 | |
1413 | 1452 | "chownr": ["[email protected]", "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], |
1414 | 1453 | |
| @@ -1462,6 +1501,8 @@ | ||
1462 | 1501 | |
1463 | 1502 | "conf": ["[email protected]", "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=="], |
1464 | 1503 | |
1504 | + "confbox": ["[email protected]", "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], | |
1505 | + | |
1465 | 1506 | "consola": ["[email protected]", "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], |
1466 | 1507 | |
1467 | 1508 | "convert-source-map": ["[email protected]", "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], |
| @@ -1478,10 +1519,16 @@ | ||
1478 | 1519 | |
1479 | 1520 | "cross-spawn": ["[email protected]", "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=="], |
1480 | 1521 | |
1522 | + "css-select": ["[email protected]", "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=="], | |
1523 | + | |
1481 | 1524 | "css-tree": ["[email protected]", "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=="], |
1482 | 1525 | |
1526 | + "css-what": ["[email protected]", "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], | |
1527 | + | |
1483 | 1528 | "cssesc": ["[email protected]", "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], |
1484 | 1529 | |
1530 | + "csso": ["[email protected]", "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], | |
1531 | + | |
1485 | 1532 | "cssstyle": ["[email protected]", "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=="], |
1486 | 1533 | |
1487 | 1534 | "csstype": ["[email protected]", "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], |
| @@ -1562,7 +1609,7 @@ | ||
1562 | 1609 | |
1563 | 1610 | "destroy": ["[email protected]", "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], |
1564 | 1611 | |
1565 | - "detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], | |
1612 | + "detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], | |
1566 | 1613 | |
1567 | 1614 | "detect-node-es": ["[email protected]", "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], |
1568 | 1615 | |
| @@ -1586,10 +1633,20 @@ | ||
1586 | 1633 | |
1587 | 1634 | "dockerode": ["[email protected]", "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=="], |
1588 | 1635 | |
1636 | + "dom-serializer": ["[email protected]", "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=="], | |
1637 | + | |
1638 | + "domelementtype": ["[email protected]", "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], | |
1639 | + | |
1640 | + "domhandler": ["[email protected]", "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], | |
1641 | + | |
1589 | 1642 | "dompurify": ["[email protected]", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.8.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ=="], |
1590 | 1643 | |
1644 | + "domutils": ["[email protected]", "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=="], | |
1645 | + | |
1591 | 1646 | "dot-prop": ["[email protected]", "https://registry.npmmirror.com/dot-prop/-/dot-prop-8.0.2.tgz", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="], |
1592 | 1647 | |
1648 | + "dotenv": ["[email protected]", "https://registry.npmmirror.com/dotenv/-/dotenv-17.4.2.tgz", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], | |
1649 | + | |
1593 | 1650 | "drizzle-kit": ["[email protected]", "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=="], |
1594 | 1651 | |
1595 | 1652 | "drizzle-orm": ["[email protected]", "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 @@ | ||
1602 | 1659 | |
1603 | 1660 | "eastasianwidth": ["[email protected]", "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], |
1604 | 1661 | |
1662 | + "ecdsa-sig-formatter": ["[email protected]", "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], | |
1663 | + | |
1605 | 1664 | "ee-first": ["[email protected]", "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], |
1606 | 1665 | |
1607 | 1666 | "electron-to-chromium": ["[email protected]", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.366.tgz", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="], |
| @@ -1616,6 +1675,8 @@ | ||
1616 | 1675 | |
1617 | 1676 | "encoding": ["[email protected]", "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], |
1618 | 1677 | |
1678 | + "encoding-sniffer": ["[email protected]", "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=="], | |
1679 | + | |
1619 | 1680 | "end-of-stream": ["[email protected]", "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=="], |
1620 | 1681 | |
1621 | 1682 | "entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-8.0.0.tgz", {}, "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA=="], |
| @@ -1662,10 +1723,14 @@ | ||
1662 | 1723 | |
1663 | 1724 | "exponential-backoff": ["[email protected]", "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], |
1664 | 1725 | |
1726 | + "exsolve": ["[email protected]", "https://registry.npmmirror.com/exsolve/-/exsolve-1.1.0.tgz", {}, "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw=="], | |
1727 | + | |
1665 | 1728 | "extend": ["[email protected]", "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], |
1666 | 1729 | |
1667 | 1730 | "extend-shallow": ["[email protected]", "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], |
1668 | 1731 | |
1732 | + "extract-zip": ["[email protected]", "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=="], | |
1733 | + | |
1669 | 1734 | "fast-copy": ["[email protected]", "https://registry.npmmirror.com/fast-copy/-/fast-copy-4.0.3.tgz", {}, "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw=="], |
1670 | 1735 | |
1671 | 1736 | "fast-deep-equal": ["[email protected]", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], |
| @@ -1684,6 +1749,8 @@ | ||
1684 | 1749 | |
1685 | 1750 | "fastq": ["[email protected]", "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], |
1686 | 1751 | |
1752 | + "fd-slicer": ["[email protected]", "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], | |
1753 | + | |
1687 | 1754 | "fdir": ["[email protected]", "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], |
1688 | 1755 | |
1689 | 1756 | "fetch-blob": ["[email protected]", "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 @@ | ||
1754 | 1821 | |
1755 | 1822 | "get-proto": ["[email protected]", "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=="], |
1756 | 1823 | |
1757 | - "get-stream": ["[email protected]", "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=="], | |
1824 | + "get-stream": ["[email protected]", "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], | |
1758 | 1825 | |
1759 | 1826 | "get-tsconfig": ["[email protected]", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], |
1760 | 1827 | |
| @@ -1768,8 +1835,12 @@ | ||
1768 | 1835 | |
1769 | 1836 | "glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], |
1770 | 1837 | |
1838 | + "globals": ["[email protected]", "https://registry.npmmirror.com/globals/-/globals-15.15.0.tgz", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], | |
1839 | + | |
1771 | 1840 | "globe.gl": ["[email protected]", "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=="], |
1772 | 1841 | |
1842 | + "google-auth-library": ["[email protected]", "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=="], | |
1843 | + | |
1773 | 1844 | "google-logging-utils": ["[email protected]", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], |
1774 | 1845 | |
1775 | 1846 | "gopd": ["[email protected]", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], |
| @@ -1822,12 +1893,16 @@ | ||
1822 | 1893 | |
1823 | 1894 | "help-me": ["[email protected]", "https://registry.npmmirror.com/help-me/-/help-me-5.0.0.tgz", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], |
1824 | 1895 | |
1896 | + "highlight.js": ["[email protected]", "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], | |
1897 | + | |
1825 | 1898 | "html-encoding-sniffer": ["[email protected]", "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=="], |
1826 | 1899 | |
1827 | 1900 | "html-escaper": ["[email protected]", "https://registry.npmmirror.com/html-escaper/-/html-escaper-3.0.3.tgz", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], |
1828 | 1901 | |
1829 | 1902 | "html-void-elements": ["[email protected]", "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], |
1830 | 1903 | |
1904 | + "htmlparser2": ["[email protected]", "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=="], | |
1905 | + | |
1831 | 1906 | "http-cache-semantics": ["[email protected]", "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], |
1832 | 1907 | |
1833 | 1908 | "http-errors": ["[email protected]", "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 @@ | ||
1952 | 2027 | |
1953 | 2028 | "jsonfile": ["[email protected]", "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], |
1954 | 2029 | |
2030 | + "jwa": ["[email protected]", "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=="], | |
2031 | + | |
2032 | + "jws": ["[email protected]", "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=="], | |
2033 | + | |
1955 | 2034 | "kapsule": ["[email protected]", "https://registry.npmmirror.com/kapsule/-/kapsule-1.16.3.tgz", { "dependencies": { "lodash-es": "4" } }, "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg=="], |
1956 | 2035 | |
1957 | 2036 | "keyv": ["[email protected]", "https://registry.npmmirror.com/keyv/-/keyv-5.6.0.tgz", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], |
| @@ -1960,6 +2039,8 @@ | ||
1960 | 2039 | |
1961 | 2040 | "kleur": ["[email protected]", "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], |
1962 | 2041 | |
2042 | + "kolorist": ["[email protected]", "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], | |
2043 | + | |
1963 | 2044 | "libsql": ["[email protected]", "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=="], |
1964 | 2045 | |
1965 | 2046 | "lightningcss": ["[email protected]", "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 @@ | ||
1992 | 2073 | |
1993 | 2074 | "load-yaml-file": ["[email protected]", "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=="], |
1994 | 2075 | |
2076 | + "local-pkg": ["[email protected]", "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=="], | |
2077 | + | |
1995 | 2078 | "locate-path": ["[email protected]", "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], |
1996 | 2079 | |
1997 | 2080 | "lodash": ["[email protected]", "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], |
| @@ -2014,6 +2097,8 @@ | ||
2014 | 2097 | |
2015 | 2098 | "lowercase-keys": ["[email protected]", "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="], |
2016 | 2099 | |
2100 | + "lowlight": ["[email protected]", "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=="], | |
2101 | + | |
2017 | 2102 | "lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], |
2018 | 2103 | |
2019 | 2104 | "lru.min": ["[email protected]", "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.4.tgz", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], |
| @@ -2156,6 +2241,8 @@ | ||
2156 | 2241 | |
2157 | 2242 | "mkdirp-classic": ["[email protected]", "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], |
2158 | 2243 | |
2244 | + "mlly": ["[email protected]", "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=="], | |
2245 | + | |
2159 | 2246 | "module-details-from-path": ["[email protected]", "https://registry.npmmirror.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="], |
2160 | 2247 | |
2161 | 2248 | "monaco-editor": ["[email protected]", "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.50.0.tgz", {}, "sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA=="], |
| @@ -2220,6 +2307,8 @@ | ||
2220 | 2307 | |
2221 | 2308 | "normalize-url": ["[email protected]", "https://registry.npmmirror.com/normalize-url/-/normalize-url-8.1.1.tgz", {}, "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ=="], |
2222 | 2309 | |
2310 | + "nth-check": ["[email protected]", "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], | |
2311 | + | |
2223 | 2312 | "oauth4webapi": ["[email protected]", "https://registry.npmmirror.com/oauth4webapi/-/oauth4webapi-3.8.6.tgz", {}, "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ=="], |
2224 | 2313 | |
2225 | 2314 | "object-assign": ["[email protected]", "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], |
| @@ -2260,18 +2349,26 @@ | ||
2260 | 2349 | |
2261 | 2350 | "p-queue": ["[email protected]", "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=="], |
2262 | 2351 | |
2352 | + "p-retry": ["[email protected]", "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="], | |
2353 | + | |
2263 | 2354 | "p-timeout": ["[email protected]", "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.4.tgz", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], |
2264 | 2355 | |
2265 | 2356 | "p-try": ["[email protected]", "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], |
2266 | 2357 | |
2267 | 2358 | "package-json-from-dist": ["[email protected]", "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], |
2268 | 2359 | |
2360 | + "package-manager-detector": ["[email protected]", "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.7.0.tgz", {}, "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ=="], | |
2361 | + | |
2269 | 2362 | "pako": ["[email protected]", "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], |
2270 | 2363 | |
2271 | 2364 | "parse-latin": ["[email protected]", "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=="], |
2272 | 2365 | |
2273 | 2366 | "parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-8.0.1.tgz", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="], |
2274 | 2367 | |
2368 | + "parse5-htmlparser2-tree-adapter": ["[email protected]", "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=="], | |
2369 | + | |
2370 | + "parse5-parser-stream": ["[email protected]", "https://registry.npmmirror.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], | |
2371 | + | |
2275 | 2372 | "path-browserify": ["[email protected]", "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], |
2276 | 2373 | |
2277 | 2374 | "path-exists": ["[email protected]", "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], |
| @@ -2288,6 +2385,8 @@ | ||
2288 | 2385 | |
2289 | 2386 | "pathe": ["[email protected]", "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], |
2290 | 2387 | |
2388 | + "pend": ["[email protected]", "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], | |
2389 | + | |
2291 | 2390 | "pg": ["[email protected]", "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=="], |
2292 | 2391 | |
2293 | 2392 | "pg-cloudflare": ["[email protected]", "https://registry.npmmirror.com/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="], |
| @@ -2326,6 +2425,8 @@ | ||
2326 | 2425 | |
2327 | 2426 | "pkg-dir": ["[email protected]", "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=="], |
2328 | 2427 | |
2428 | + "pkg-types": ["[email protected]", "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=="], | |
2429 | + | |
2329 | 2430 | "playwright": ["[email protected]", "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=="], |
2330 | 2431 | |
2331 | 2432 | "playwright-core": ["[email protected]", "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 @@ | ||
2338 | 2439 | |
2339 | 2440 | "possible-typed-array-names": ["[email protected]", "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], |
2340 | 2441 | |
2341 | - "postcss": ["[email protected]", "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=="], | |
2442 | + "postcss": ["[email protected]", "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=="], | |
2342 | 2443 | |
2343 | 2444 | "postcss-import": ["[email protected]", "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=="], |
2344 | 2445 | |
| @@ -2398,6 +2499,8 @@ | ||
2398 | 2499 | |
2399 | 2500 | "qrcode": ["[email protected]", "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=="], |
2400 | 2501 | |
2502 | + "quansync": ["[email protected]", "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], | |
2503 | + | |
2401 | 2504 | "queue-microtask": ["[email protected]", "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], |
2402 | 2505 | |
2403 | 2506 | "quick-format-unescaped": ["[email protected]", "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], |
| @@ -2432,7 +2535,7 @@ | ||
2432 | 2535 | |
2433 | 2536 | "readable-stream": ["[email protected]", "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=="], |
2434 | 2537 | |
2435 | - "readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], | |
2538 | + "readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], | |
2436 | 2539 | |
2437 | 2540 | "real-require": ["[email protected]", "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], |
2438 | 2541 | |
| @@ -2454,6 +2557,8 @@ | ||
2454 | 2557 | |
2455 | 2558 | "rehype": ["[email protected]", "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=="], |
2456 | 2559 | |
2560 | + "rehype-highlight": ["[email protected]", "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=="], | |
2561 | + | |
2457 | 2562 | "rehype-parse": ["[email protected]", "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=="], |
2458 | 2563 | |
2459 | 2564 | "rehype-raw": ["[email protected]", "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 @@ | ||
2504 | 2609 | |
2505 | 2610 | "retext-stringify": ["[email protected]", "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=="], |
2506 | 2611 | |
2507 | - "retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], | |
2612 | + "retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], | |
2508 | 2613 | |
2509 | 2614 | "reusify": ["[email protected]", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], |
2510 | 2615 | |
| @@ -2526,6 +2631,8 @@ | ||
2526 | 2631 | |
2527 | 2632 | "safer-buffer": ["[email protected]", "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], |
2528 | 2633 | |
2634 | + "sax": ["[email protected]", "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], | |
2635 | + | |
2529 | 2636 | "saxes": ["[email protected]", "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], |
2530 | 2637 | |
2531 | 2638 | "scheduler": ["[email protected]", "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 @@ | ||
2648 | 2755 | |
2649 | 2756 | "supports-preserve-symlinks-flag": ["[email protected]", "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], |
2650 | 2757 | |
2758 | + "svgo": ["[email protected]", "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=="], | |
2759 | + | |
2651 | 2760 | "symbol-tree": ["[email protected]", "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], |
2652 | 2761 | |
2653 | 2762 | "tailwind-merge": ["[email protected]", "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-2.6.1.tgz", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="], |
| @@ -2742,6 +2851,8 @@ | ||
2742 | 2851 | |
2743 | 2852 | "typescript-auto-import-cache": ["[email protected]", "https://registry.npmmirror.com/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="], |
2744 | 2853 | |
2854 | + "ufo": ["[email protected]", "https://registry.npmmirror.com/ufo/-/ufo-1.6.4.tgz", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], | |
2855 | + | |
2745 | 2856 | "uint8array-extras": ["[email protected]", "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-0.3.0.tgz", {}, "sha512-erJsJwQ0tKdwuqI0359U8ijkFmfiTcq25JvvzRVc1VP+2son1NJRXhxcAKJmAW3ajM8JSGAfsAXye8g4s+znxA=="], |
2746 | 2857 | |
2747 | 2858 | "ultrahtml": ["[email protected]", "https://registry.npmmirror.com/ultrahtml/-/ultrahtml-1.6.0.tgz", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="], |
| @@ -2800,7 +2911,7 @@ | ||
2800 | 2911 | |
2801 | 2912 | "victory-vendor": ["[email protected]", "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=="], |
2802 | 2913 | |
2803 | - "vite": ["[email protected]", "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=="], | |
2914 | + "vite": ["[email protected]", "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=="], | |
2804 | 2915 | |
2805 | 2916 | "vitefu": ["[email protected]", "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=="], |
2806 | 2917 | |
| @@ -2852,6 +2963,8 @@ | ||
2852 | 2963 | |
2853 | 2964 | "webidl-conversions": ["[email protected]", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="], |
2854 | 2965 | |
2966 | + "whatwg-encoding": ["[email protected]", "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], | |
2967 | + | |
2855 | 2968 | "whatwg-mimetype": ["[email protected]", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], |
2856 | 2969 | |
2857 | 2970 | "whatwg-url": ["[email protected]", "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 @@ | ||
2902 | 3015 | |
2903 | 3016 | "yargs-parser": ["[email protected]", "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], |
2904 | 3017 | |
3018 | + "yauzl": ["[email protected]", "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=="], | |
3019 | + | |
2905 | 3020 | "yocto-queue": ["[email protected]", "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.2.2.tgz", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], |
2906 | 3021 | |
2907 | 3022 | "yoctocolors-cjs": ["[email protected]", "https://registry.npmmirror.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="], |
| @@ -2918,10 +3033,6 @@ | ||
2918 | 3033 | |
2919 | 3034 | "@asamuzakjp/dom-selector/lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], |
2920 | 3035 | |
2921 | - "@astrojs/check/chokidar": ["[email protected]", "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], | |
2922 | - | |
2923 | - "@astrojs/react/vite": ["[email protected]", "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=="], | |
2924 | - | |
2925 | 3036 | "@astrojs/vercel/esbuild": ["[email protected]", "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=="], |
2926 | 3037 | |
2927 | 3038 | "@babel/code-frame/js-tokens": ["[email protected]", "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], |
| @@ -2944,8 +3055,6 @@ | ||
2944 | 3055 | |
2945 | 3056 | "@isaacs/cliui/strip-ansi": ["[email protected]", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], |
2946 | 3057 | |
2947 | - "@mapbox/node-pre-gyp/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], | |
2948 | - | |
2949 | 3058 | "@mapbox/node-pre-gyp/node-fetch": ["[email protected]", "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=="], |
2950 | 3059 | |
2951 | 3060 | "@opentelemetry/instrumentation-pg/@types/pg": ["@types/[email protected]", "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 @@ | ||
2998 | 3107 | |
2999 | 3108 | "@vercel/nft/estree-walker": ["[email protected]", "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], |
3000 | 3109 | |
3001 | - "@vitejs/plugin-react/vite": ["[email protected]", "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=="], | |
3002 | - | |
3003 | 3110 | "ansi-align/string-width": ["[email protected]", "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=="], |
3004 | 3111 | |
3005 | 3112 | "ansi-escapes/type-fest": ["[email protected]", "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], |
| @@ -3016,8 +3123,6 @@ | ||
3016 | 3123 | |
3017 | 3124 | "astro/tinyexec": ["[email protected]", "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], |
3018 | 3125 | |
3019 | - "astro/vite": ["[email protected]", "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=="], | |
3020 | - | |
3021 | 3126 | "bl/readable-stream": ["[email protected]", "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=="], |
3022 | 3127 | |
3023 | 3128 | "bullmq/ioredis": ["[email protected]", "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 @@ | ||
3026 | 3131 | |
3027 | 3132 | "cacache/glob": ["[email protected]", "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=="], |
3028 | 3133 | |
3134 | + "cacheable-request/get-stream": ["[email protected]", "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=="], | |
3135 | + | |
3029 | 3136 | "cacheable-request/mimic-response": ["[email protected]", "https://registry.npmmirror.com/mimic-response/-/mimic-response-4.0.0.tgz", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], |
3030 | 3137 | |
3031 | - "chokidar/glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], | |
3138 | + "cheerio/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], | |
3139 | + | |
3140 | + "cheerio/whatwg-mimetype": ["[email protected]", "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], | |
3032 | 3141 | |
3033 | 3142 | "cli-progress/string-width": ["[email protected]", "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=="], |
3034 | 3143 | |
| @@ -3044,14 +3153,20 @@ | ||
3044 | 3153 | |
3045 | 3154 | "cross-spawn/which": ["[email protected]", "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=="], |
3046 | 3155 | |
3156 | + "csso/css-tree": ["[email protected]", "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=="], | |
3157 | + | |
3047 | 3158 | "cssstyle/lru-cache": ["[email protected]", "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], |
3048 | 3159 | |
3049 | 3160 | "docker-modem/readable-stream": ["[email protected]", "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=="], |
3050 | 3161 | |
3162 | + "dom-serializer/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], | |
3163 | + | |
3051 | 3164 | "dot-prop/type-fest": ["[email protected]", "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="], |
3052 | 3165 | |
3053 | 3166 | "encoding/iconv-lite": ["[email protected]", "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=="], |
3054 | 3167 | |
3168 | + "encoding-sniffer/iconv-lite": ["[email protected]", "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=="], | |
3169 | + | |
3055 | 3170 | "fast-glob/glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], |
3056 | 3171 | |
3057 | 3172 | "figlet/commander": ["[email protected]", "https://registry.npmmirror.com/commander/-/commander-14.0.3.tgz", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], |
| @@ -3064,12 +3179,14 @@ | ||
3064 | 3179 | |
3065 | 3180 | "gradient-string/chalk": ["[email protected]", "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=="], |
3066 | 3181 | |
3067 | - "gray-matter/js-yaml": ["[email protected]", "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=="], | |
3182 | + "gray-matter/js-yaml": ["[email protected]", "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=="], | |
3068 | 3183 | |
3069 | 3184 | "hast-util-from-html/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], |
3070 | 3185 | |
3071 | 3186 | "hast-util-raw/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], |
3072 | 3187 | |
3188 | + "htmlparser2/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], | |
3189 | + | |
3073 | 3190 | "inquirer/ora": ["[email protected]", "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=="], |
3074 | 3191 | |
3075 | 3192 | "inquirer/string-width": ["[email protected]", "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 @@ | ||
3078 | 3195 | |
3079 | 3196 | "istanbul-reports/html-escaper": ["[email protected]", "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], |
3080 | 3197 | |
3081 | - "lightningcss/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], | |
3198 | + "libsql/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], | |
3082 | 3199 | |
3083 | - "load-yaml-file/js-yaml": ["[email protected]", "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=="], | |
3200 | + "load-yaml-file/js-yaml": ["[email protected]", "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=="], | |
3084 | 3201 | |
3085 | 3202 | "log-symbols/is-unicode-supported": ["[email protected]", "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], |
3086 | 3203 | |
| @@ -3092,9 +3209,9 @@ | ||
3092 | 3209 | |
3093 | 3210 | "minipass-sized/minipass": ["[email protected]", "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], |
3094 | 3211 | |
3095 | - "node-gyp/env-paths": ["[email protected]", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], | |
3212 | + "mlly/pkg-types": ["[email protected]", "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=="], | |
3096 | 3213 | |
3097 | - "node-gyp-build-optional-packages/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], | |
3214 | + "node-gyp/env-paths": ["[email protected]", "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], | |
3098 | 3215 | |
3099 | 3216 | "nodegit/tar-fs": ["[email protected]", "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=="], |
3100 | 3217 | |
| @@ -3106,13 +3223,17 @@ | ||
3106 | 3223 | |
3107 | 3224 | "p-locate/p-limit": ["[email protected]", "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], |
3108 | 3225 | |
3226 | + "parse5-htmlparser2-tree-adapter/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], | |
3227 | + | |
3228 | + "parse5-parser-stream/parse5": ["[email protected]", "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], | |
3229 | + | |
3109 | 3230 | "playwright/fsevents": ["[email protected]", "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], |
3110 | 3231 | |
3111 | - "postcss/nanoid": ["[email protected]", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], | |
3232 | + "postcss/nanoid": ["[email protected]", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.16.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], | |
3112 | 3233 | |
3113 | 3234 | "postcss-nested/postcss-selector-parser": ["[email protected]", "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=="], |
3114 | 3235 | |
3115 | - "prebuild-install/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], | |
3236 | + "promise-retry/retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], | |
3116 | 3237 | |
3117 | 3238 | "prompts/kleur": ["[email protected]", "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], |
3118 | 3239 | |
| @@ -3128,8 +3249,6 @@ | ||
3128 | 3249 | |
3129 | 3250 | "send/debug": ["[email protected]", "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], |
3130 | 3251 | |
3131 | - "sharp/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], | |
3132 | - | |
3133 | 3252 | "ssh2/nan": ["[email protected]", "https://registry.npmmirror.com/nan/-/nan-2.27.0.tgz", {}, "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ=="], |
3134 | 3253 | |
3135 | 3254 | "stream-browserify/readable-stream": ["[email protected]", "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 @@ | ||
3140 | 3259 | |
3141 | 3260 | "sucrase/commander": ["[email protected]", "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], |
3142 | 3261 | |
3262 | + "svgo/commander": ["[email protected]", "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], | |
3263 | + | |
3264 | + "svgo/css-tree": ["[email protected]", "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=="], | |
3265 | + | |
3266 | + "tailwindcss/chokidar": ["[email protected]", "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=="], | |
3267 | + | |
3143 | 3268 | "tailwindcss/postcss-selector-parser": ["[email protected]", "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=="], |
3144 | 3269 | |
3145 | 3270 | "tar/chownr": ["[email protected]", "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], |
| @@ -3150,12 +3275,18 @@ | ||
3150 | 3275 | |
3151 | 3276 | "tsx/esbuild": ["[email protected]", "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=="], |
3152 | 3277 | |
3278 | + "vite/esbuild": ["[email protected]", "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=="], | |
3279 | + | |
3280 | + "vitest/vite": ["[email protected]", "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=="], | |
3281 | + | |
3153 | 3282 | "vscode-css-languageservice/vscode-languageserver-types": ["[email protected]", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], |
3154 | 3283 | |
3155 | 3284 | "vscode-json-languageservice/jsonc-parser": ["[email protected]", "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], |
3156 | 3285 | |
3157 | 3286 | "vscode-languageserver-protocol/vscode-languageserver-types": ["[email protected]", "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], |
3158 | 3287 | |
3288 | + "whatwg-encoding/iconv-lite": ["[email protected]", "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=="], | |
3289 | + | |
3159 | 3290 | "wrap-ansi/strip-ansi": ["[email protected]", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], |
3160 | 3291 | |
3161 | 3292 | "wrap-ansi-cjs/ansi-styles": ["[email protected]", "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 @@ | ||
3164 | 3295 | |
3165 | 3296 | "yaml-language-server/request-light": ["[email protected]", "https://registry.npmmirror.com/request-light/-/request-light-0.5.8.tgz", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], |
3166 | 3297 | |
3167 | - "yargs/string-width": ["[email protected]", "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=="], | |
3168 | - | |
3169 | - "@astrojs/check/chokidar/readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], | |
3298 | + "yaml-language-server/yaml": ["[email protected]", "https://registry.npmmirror.com/yaml/-/yaml-2.7.1.tgz", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], | |
3170 | 3299 | |
3171 | - "@astrojs/react/vite/esbuild": ["[email protected]", "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=="], | |
3300 | + "yargs/string-width": ["[email protected]", "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=="], | |
3172 | 3301 | |
3173 | 3302 | "@astrojs/vercel/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], |
3174 | 3303 | |
| @@ -3268,8 +3397,6 @@ | ||
3268 | 3397 | |
3269 | 3398 | "@types/ssh2/@types/node/undici-types": ["[email protected]", "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], |
3270 | 3399 | |
3271 | - "@vitejs/plugin-react/vite/esbuild": ["[email protected]", "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=="], | |
3272 | - | |
3273 | 3400 | "ansi-align/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], |
3274 | 3401 | |
3275 | 3402 | "astro/boxen/camelcase": ["[email protected]", "https://registry.npmmirror.com/camelcase/-/camelcase-8.0.0.tgz", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], |
| @@ -3330,6 +3457,8 @@ | ||
3330 | 3457 | |
3331 | 3458 | "bullmq/ioredis/@ioredis/commands": ["@ioredis/[email protected]", "https://registry.npmmirror.com/@ioredis/commands/-/commands-1.5.1.tgz", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="], |
3332 | 3459 | |
3460 | + "cheerio/parse5/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], | |
3461 | + | |
3333 | 3462 | "cli-progress/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], |
3334 | 3463 | |
3335 | 3464 | "cli-table3/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], |
| @@ -3340,6 +3469,8 @@ | ||
3340 | 3469 | |
3341 | 3470 | "cross-spawn/which/isexe": ["[email protected]", "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], |
3342 | 3471 | |
3472 | + "csso/css-tree/mdn-data": ["[email protected]", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], | |
3473 | + | |
3343 | 3474 | "got/decompress-response/mimic-response": ["[email protected]", "https://registry.npmmirror.com/mimic-response/-/mimic-response-4.0.0.tgz", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="], |
3344 | 3475 | |
3345 | 3476 | "gradient-string/chalk/ansi-styles": ["[email protected]", "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 @@ | ||
3372 | 3503 | |
3373 | 3504 | "minipass-sized/minipass/yallist": ["[email protected]", "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], |
3374 | 3505 | |
3506 | + "mlly/pkg-types/confbox": ["[email protected]", "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], | |
3507 | + | |
3375 | 3508 | "nodegit/tar-fs/tar-stream": ["[email protected]", "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=="], |
3376 | 3509 | |
3377 | 3510 | "ora/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], |
3378 | 3511 | |
3379 | 3512 | "ora/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], |
3380 | 3513 | |
3514 | + "parse5-htmlparser2-tree-adapter/parse5/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], | |
3515 | + | |
3516 | + "parse5-parser-stream/parse5/entities": ["[email protected]", "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], | |
3517 | + | |
3381 | 3518 | "qrcode/yargs/cliui": ["[email protected]", "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=="], |
3382 | 3519 | |
3383 | 3520 | "qrcode/yargs/string-width": ["[email protected]", "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 @@ | ||
3390 | 3527 | |
3391 | 3528 | "string-width/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], |
3392 | 3529 | |
3530 | + "svgo/css-tree/mdn-data": ["[email protected]", "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], | |
3531 | + | |
3532 | + "tailwindcss/chokidar/glob-parent": ["[email protected]", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], | |
3533 | + | |
3534 | + "tailwindcss/chokidar/readdirp": ["[email protected]", "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], | |
3535 | + | |
3393 | 3536 | "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], |
3394 | 3537 | |
3395 | 3538 | "tsx/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "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 @@ | ||
3442 | 3585 | |
3443 | 3586 | "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], |
3444 | 3587 | |
3445 | - "wrap-ansi-cjs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], | |
3588 | + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], | |
3446 | 3589 | |
3447 | - "wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], | |
3590 | + "vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], | |
3448 | 3591 | |
3449 | - "yargs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], | |
3592 | + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], | |
3450 | 3593 | |
3451 | - "@astrojs/react/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], | |
3594 | + "vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], | |
3452 | 3595 | |
3453 | - "@astrojs/react/vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], | |
3596 | + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], | |
3454 | 3597 | |
3455 | - "@astrojs/react/vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], | |
3598 | + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], | |
3456 | 3599 | |
3457 | - "@astrojs/react/vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], | |
3600 | + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], | |
3458 | 3601 | |
3459 | - "@astrojs/react/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], | |
3602 | + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], | |
3460 | 3603 | |
3461 | - "@astrojs/react/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], | |
3604 | + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], | |
3462 | 3605 | |
3463 | - "@astrojs/react/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], | |
3606 | + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], | |
3464 | 3607 | |
3465 | - "@astrojs/react/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], | |
3608 | + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], | |
3466 | 3609 | |
3467 | - "@astrojs/react/vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], | |
3610 | + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], | |
3468 | 3611 | |
3469 | - "@astrojs/react/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], | |
3612 | + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], | |
3470 | 3613 | |
3471 | - "@astrojs/react/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], | |
3614 | + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], | |
3472 | 3615 | |
3473 | - "@astrojs/react/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], | |
3616 | + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], | |
3474 | 3617 | |
3475 | - "@astrojs/react/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], | |
3618 | + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], | |
3476 | 3619 | |
3477 | - "@astrojs/react/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], | |
3620 | + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], | |
3478 | 3621 | |
3479 | - "@astrojs/react/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], | |
3622 | + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], | |
3480 | 3623 | |
3481 | - "@astrojs/react/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], | |
3624 | + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], | |
3482 | 3625 | |
3483 | - "@astrojs/react/vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], | |
3626 | + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], | |
3484 | 3627 | |
3485 | - "@astrojs/react/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], | |
3628 | + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], | |
3486 | 3629 | |
3487 | - "@astrojs/react/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], | |
3630 | + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], | |
3488 | 3631 | |
3489 | - "@astrojs/react/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], | |
3632 | + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], | |
3490 | 3633 | |
3491 | - "@astrojs/react/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], | |
3634 | + "wrap-ansi-cjs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], | |
3492 | 3635 | |
3493 | - "@astrojs/react/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], | |
3636 | + "wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], | |
3494 | 3637 | |
3495 | - "@astrojs/react/vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], | |
3638 | + "yargs/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], | |
3496 | 3639 | |
3497 | 3640 | "@mapbox/node-pre-gyp/node-fetch/whatwg-url/tr46": ["[email protected]", "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], |
3498 | 3641 | |
3499 | 3642 | "@mapbox/node-pre-gyp/node-fetch/whatwg-url/webidl-conversions": ["[email protected]", "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], |
3500 | 3643 | |
3501 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], | |
3502 | - | |
3503 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], | |
3504 | - | |
3505 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], | |
3506 | - | |
3507 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], | |
3508 | - | |
3509 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], | |
3510 | - | |
3511 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], | |
3512 | - | |
3513 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], | |
3514 | - | |
3515 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], | |
3516 | - | |
3517 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], | |
3518 | - | |
3519 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], | |
3520 | - | |
3521 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], | |
3522 | - | |
3523 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], | |
3524 | - | |
3525 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], | |
3526 | - | |
3527 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], | |
3528 | - | |
3529 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], | |
3530 | - | |
3531 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], | |
3532 | - | |
3533 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], | |
3534 | - | |
3535 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], | |
3536 | - | |
3537 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], | |
3538 | - | |
3539 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], | |
3540 | - | |
3541 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], | |
3542 | - | |
3543 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], | |
3544 | - | |
3545 | - "@vitejs/plugin-react/vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], | |
3546 | - | |
3547 | 3644 | "astro/boxen/string-width/emoji-regex": ["[email protected]", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], |
3548 | 3645 | |
3549 | 3646 | "astro/boxen/string-width/strip-ansi": ["[email protected]", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], |
3550 | 3647 | |
▼cli/src/commands/stack/index.ts
+0-4
| @@ -270,10 +270,6 @@ stackCommands | ||
270 | 270 | .action(async () => { |
271 | 271 | try { |
272 | 272 | const roots = await getStackTopology(git); |
273 | - console.log(chalk.hex("#6272a4")("No stack found.")); | |
274 | - return; | |
275 | - } | |
276 | - | |
277 | 273 | const { printSectionHeader, formatBadge } = await import("../../lib/formatter.js"); |
278 | 274 | |
279 | 275 | // Use Dracula colors directly or via chalk |
280 | 276 | |
▼deploy/prometheus.yml
+6-0
| @@ -6,6 +6,12 @@ scrape_configs: | ||
6 | 6 | metrics_path: "/api/metrics" |
7 | 7 | scrape_interval: 15s |
8 | 8 | scrape_timeout: 10s |
9 | + # /api/metrics is protected by METRICS_TOKEN when set. Provide the token | |
10 | + # via environment expansion, e.g. start prometheus with: | |
11 | + # METRICS_TOKEN=<value> prometheus --config.file=prometheus.yml | |
12 | + # or mount a file containing the token at /etc/prometheus/och-metrics-token | |
13 | + authorization: | |
14 | + credentials_file: /etc/prometheus/och-metrics-token | |
9 | 15 | static_configs: |
10 | 16 | - targets: ["opencodehub-app:4321"] |
11 | 17 | labels: |
12 | 18 | |
▼doc/docs-parity-baseline.json
+2-0
| @@ -1,5 +1,7 @@ | ||
1 | 1 | { |
2 | 2 | "missingInSite": [ |
3 | + "cli", | |
4 | + "getting-started/ssh-keys", | |
3 | 5 | "support/troubleshooting" |
4 | 6 | ], |
5 | 7 | "missingInDocs": [ |
6 | 8 | |
▼docker-compose.nas.yml
+129-67
| @@ -1,78 +1,158 @@ | ||
1 | +# OpenCodeHub — NAS / home-server stack | |
2 | +# ============================================================ | |
3 | +# Runs everything on a dedicated disk or NAS share by binding every | |
4 | +# data directory to a host path you control (default /mnt/och). | |
5 | +# | |
6 | +# 1. cp .env.example .env | |
7 | +# 2. Set required secrets (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, | |
8 | +# CRON_SECRET, RUNNER_SECRET, AI_CONFIG_ENCRYPTION_KEY, | |
9 | +# WORKFLOW_SECRET_ENCRYPTION_KEY, POSTGRES_PASSWORD, REDIS_PASSWORD) | |
10 | +# 3. (Optional) point OCH_DATA_DIR at your NAS mount or dedicated disk: | |
11 | +# OCH_DATA_DIR=/mnt/nas/opencodehub # or /volume1/opencodehub on Synology | |
12 | +# 4. docker compose -f docker-compose.nas.yml up -d | |
13 | +# | |
14 | +# Storage backends (local + S3 only): | |
15 | +# - local: STORAGE_TYPE=local, everything under ${OCH_DATA_DIR}/storage | |
16 | +# - s3: STORAGE_TYPE=s3 with STORAGE_ENDPOINT/BUCKET/ACCESS_KEY/SECRET | |
17 | +# | |
18 | +# Optional profiles: | |
19 | +# --profile cloudflare Cloudflare Tunnel for external access | |
20 | +# --profile tailscale Tailscale sidecar | |
21 | +# ============================================================ | |
22 | + | |
23 | +x-och-env: &och-env | |
24 | + NODE_ENV: production | |
25 | + DATABASE_DRIVER: postgres | |
26 | + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} | |
27 | + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 | |
28 | + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} | |
29 | + SESSION_SECRET: ${SESSION_SECRET:?Set SESSION_SECRET in .env} | |
30 | + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} | |
31 | + CRON_SECRET: ${CRON_SECRET:?Set CRON_SECRET in .env} | |
32 | + RUNNER_SECRET: ${RUNNER_SECRET:?Set RUNNER_SECRET in .env} | |
33 | + AI_CONFIG_ENCRYPTION_KEY: ${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env} | |
34 | + WORKFLOW_SECRET_ENCRYPTION_KEY: ${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env} | |
35 | + SITE_URL: ${SITE_URL:-http://localhost:4321} | |
36 | + ALLOWED_ORIGINS: ${SITE_URL:-http://localhost:4321} | |
37 | + STORAGE_TYPE: ${STORAGE_TYPE:-local} | |
38 | + STORAGE_PATH: /data/storage | |
39 | + GIT_REPOS_PATH: /data/repos | |
40 | + REPOS_PATH: /data/repos | |
41 | + GIT_SSH_HOST_KEY: /data/ssh/host_key | |
42 | + GIT_SSH_PORT: 2222 | |
43 | + RATE_LIMIT_ENABLED: "true" | |
44 | + RATE_LIMIT_SKIP_DEV: "false" | |
45 | + CSRF_SKIP_DEV: "false" | |
46 | + TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.1,172.16.0.0/12,100.64.0.0/10} | |
47 | + | |
48 | +x-och-common: &och-common | |
49 | + image: opencodehub/opencodehub:latest | |
50 | + restart: unless-stopped | |
51 | + environment: | |
52 | + <<: *och-env | |
53 | + volumes: | |
54 | + - ${OCH_DATA_DIR:-/mnt/och}/repos:/data/repos | |
55 | + - ${OCH_DATA_DIR:-/mnt/och}/storage:/data/storage | |
56 | + - ${OCH_DATA_DIR:-/mnt/och}/ssh:/data/ssh | |
57 | + - ${OCH_DATA_DIR:-/mnt/och}/cache:/data/cache | |
58 | + depends_on: | |
59 | + postgres: | |
60 | + condition: service_healthy | |
61 | + redis: | |
62 | + condition: service_healthy | |
63 | + networks: | |
64 | + - opencodehub-network | |
65 | + | |
1 | 66 | services: |
2 | - # Main OpenCodeHub application | |
67 | + # ── Main application (web + API + Git HTTP) ──────────────── | |
3 | 68 | app: |
4 | - image: swadhinbiswas/opencodehub:latest | |
69 | + <<: *och-common | |
5 | 70 | container_name: opencodehub |
6 | - restart: unless-stopped | |
71 | + environment: | |
72 | + <<: *och-env | |
73 | + PROCESS_TYPE: app | |
74 | + PORT: 4321 | |
7 | 75 | ports: |
8 | - - "4321:4321" # Web interface (Internal) | |
9 | - - "2222:2222" # SSH Git (Internal) | |
76 | + - "${WEB_PORT:-4321}:4321" | |
77 | + healthcheck: | |
78 | + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"] | |
79 | + interval: 30s | |
80 | + timeout: 10s | |
81 | + retries: 3 | |
82 | + start_period: 20s | |
83 | + | |
84 | + # ── SSH Git server ───────────────────────────────────────── | |
85 | + ssh-git: | |
86 | + <<: *och-common | |
87 | + container_name: opencodehub-ssh | |
10 | 88 | environment: |
11 | - - NODE_ENV=production | |
12 | - - DATABASE_URL=postgresql://opencodehub:opencodehub@postgres:5432/opencodehub | |
13 | - - DATABASE_TYPE=postgres | |
14 | - - REDIS_URL=redis://redis:6379 | |
15 | - - JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required} | |
16 | - - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:?WORKFLOW_SECRET_ENCRYPTION_KEY is required} | |
17 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
18 | - - SERVER_URL=${SITE_URL:-http://localhost:4321} | |
19 | - - STORAGE_TYPE=local | |
20 | - - STORAGE_PATH=/data/storage | |
21 | - - REPOS_PATH=/data/repos | |
22 | - # TRUSTED_PROXIES allows real IP mapping from Cloudflare or Tailscale sidecars | |
23 | - - TRUSTED_PROXIES=127.0.0.1,172.16.0.0/12,100.64.0.0/10 | |
24 | - volumes: | |
25 | - - repos-data:/data/repos | |
26 | - - storage-data:/data/storage | |
27 | - - ssh-data:/data/ssh | |
28 | - - cache-data:/data/cache | |
29 | - depends_on: | |
30 | - postgres: | |
31 | - condition: service_healthy | |
32 | - redis: | |
33 | - condition: service_healthy | |
34 | - networks: | |
35 | - - opencodehub-network | |
89 | + <<: *och-env | |
90 | + PROCESS_TYPE: ssh | |
91 | + ports: | |
92 | + - "${SSH_PORT:-2222}:2222" | |
93 | + | |
94 | + # ── Background worker ────────────────────────────────────── | |
95 | + worker: | |
96 | + <<: *och-common | |
97 | + container_name: opencodehub-worker | |
98 | + environment: | |
99 | + <<: *och-env | |
100 | + PROCESS_TYPE: worker | |
101 | + WORKER_HEALTH_PORT: 9090 | |
102 | + expose: | |
103 | + - "9090" | |
104 | + healthcheck: | |
105 | + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/healthz"] | |
106 | + interval: 30s | |
107 | + timeout: 10s | |
108 | + retries: 3 | |
109 | + start_period: 20s | |
36 | 110 | |
37 | - # PostgreSQL database | |
111 | + # ── PostgreSQL ───────────────────────────────────────────── | |
38 | 112 | postgres: |
39 | - image: postgres:16-alpine | |
113 | + image: docker.io/library/postgres:16-alpine | |
40 | 114 | container_name: opencodehub-postgres |
41 | 115 | restart: unless-stopped |
42 | 116 | environment: |
43 | - POSTGRES_USER: opencodehub | |
44 | - POSTGRES_PASSWORD: opencodehub | |
45 | - POSTGRES_DB: opencodehub | |
117 | + POSTGRES_USER: ${POSTGRES_USER:-opencodehub} | |
118 | + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} | |
119 | + POSTGRES_DB: ${POSTGRES_DB:-opencodehub} | |
46 | 120 | volumes: |
47 | - - postgres-data:/var/lib/postgresql/data | |
121 | + - ${OCH_DATA_DIR:-/mnt/och}/postgres:/var/lib/postgresql/data | |
48 | 122 | networks: |
49 | 123 | - opencodehub-network |
50 | 124 | healthcheck: |
51 | - test: ["CMD-SHELL", "pg_isready -U opencodehub -d opencodehub"] | |
125 | + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-opencodehub} -d ${POSTGRES_DB:-opencodehub}"] | |
52 | 126 | interval: 10s |
53 | 127 | timeout: 5s |
54 | 128 | retries: 5 |
55 | 129 | |
56 | - # Redis for caching and sessions | |
130 | + # ── Redis ────────────────────────────────────────────────── | |
57 | 131 | redis: |
58 | - image: redis:7-alpine | |
132 | + image: docker.io/library/redis:7-alpine | |
59 | 133 | container_name: opencodehub-redis |
60 | 134 | restart: unless-stopped |
61 | - command: redis-server --appendonly yes | |
135 | + command: | |
136 | + - redis-server | |
137 | + - --requirepass | |
138 | + - ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env} | |
139 | + - --appendonly | |
140 | + - "yes" | |
141 | + - --maxmemory | |
142 | + - 256mb | |
143 | + - --maxmemory-policy | |
144 | + - allkeys-lru | |
62 | 145 | volumes: |
63 | - - redis-data:/data | |
146 | + - ${OCH_DATA_DIR:-/mnt/och}/redis:/data | |
64 | 147 | networks: |
65 | 148 | - opencodehub-network |
66 | 149 | healthcheck: |
67 | - test: ["CMD", "redis-cli", "ping"] | |
150 | + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] | |
68 | 151 | interval: 10s |
69 | 152 | timeout: 5s |
70 | 153 | retries: 5 |
71 | 154 | |
72 | - # ------------------------------------------------------------- | |
73 | - # OPTION 1: Cloudflare Tunnel (Uncomment 'profiles' to use) | |
74 | - # Run with: docker-compose -f docker-compose.nas.yml --profile cloudflare up -d | |
75 | - # ------------------------------------------------------------- | |
155 | + # ── Cloudflare Tunnel (optional) ─────────────────────────── | |
76 | 156 | cloudflared: |
77 | 157 | image: cloudflare/cloudflared:latest |
78 | 158 | container_name: opencodehub-cloudflared |
| @@ -85,20 +165,18 @@ services: | ||
85 | 165 | profiles: |
86 | 166 | - cloudflare |
87 | 167 | |
88 | - # ------------------------------------------------------------- | |
89 | - # OPTION 2: Tailscale (Uncomment 'profiles' to use) | |
90 | - # Run with: docker-compose -f docker-compose.nas.yml --profile tailscale up -d | |
91 | - # ------------------------------------------------------------- | |
168 | + # ── Tailscale (optional) ─────────────────────────────────── | |
92 | 169 | tailscale: |
93 | 170 | image: tailscale/tailscale:latest |
94 | 171 | container_name: opencodehub-tailscale |
95 | 172 | hostname: opencodehub-nas |
173 | + restart: unless-stopped | |
96 | 174 | environment: |
97 | 175 | - TS_AUTHKEY=${TS_AUTHKEY:-} |
98 | 176 | - TS_STATE_DIR=/var/lib/tailscale |
99 | 177 | - TS_USERSPACE=false |
100 | 178 | volumes: |
101 | - - tailscale-data:/var/lib/tailscale | |
179 | + - ${OCH_DATA_DIR:-/mnt/och}/tailscale:/var/lib/tailscale | |
102 | 180 | - /dev/net/tun:/dev/net/tun |
103 | 181 | cap_add: |
104 | 182 | - net_admin |
| @@ -108,22 +186,6 @@ services: | ||
108 | 186 | profiles: |
109 | 187 | - tailscale |
110 | 188 | |
111 | -volumes: | |
112 | - repos-data: | |
113 | - driver: local | |
114 | - storage-data: | |
115 | - driver: local | |
116 | - ssh-data: | |
117 | - driver: local | |
118 | - cache-data: | |
119 | - driver: local | |
120 | - postgres-data: | |
121 | - driver: local | |
122 | - redis-data: | |
123 | - driver: local | |
124 | - tailscale-data: | |
125 | - driver: local | |
126 | - | |
127 | 189 | networks: |
128 | 190 | opencodehub-network: |
129 | 191 | driver: bridge |
130 | 192 | |
▼docker-compose.production.yml
+54-8
| @@ -49,10 +49,9 @@ services: | ||
49 | 49 | restart: unless-stopped |
50 | 50 | expose: |
51 | 51 | - "4321" # Internal only — Caddy proxies to this |
52 | - ports: | |
53 | - - "${SSH_PORT:-2222}:2222" # SSH Git (exposed directly) | |
54 | 52 | environment: |
55 | 53 | - NODE_ENV=production |
54 | + - PROCESS_TYPE=app | |
56 | 55 | - DATABASE_DRIVER=postgres |
57 | 56 | - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} |
58 | 57 | - DATABASE_SSL=false |
| @@ -64,7 +63,6 @@ services: | ||
64 | 63 | - AI_CONFIG_ENCRYPTION_KEY=${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env} |
65 | 64 | - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env} |
66 | 65 | - RUNNER_SECRET=${RUNNER_SECRET:?Set RUNNER_SECRET in .env} |
67 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
68 | 66 | - SITE_URL=${SITE_URL:-https://opencodehub.example.com} |
69 | 67 | - SERVER_URL=${SITE_URL:-https://opencodehub.example.com} |
70 | 68 | - STORAGE_TYPE=${STORAGE_TYPE:-local} |
| @@ -77,9 +75,6 @@ services: | ||
77 | 75 | - CSRF_SKIP_DEV=false |
78 | 76 | - ALLOWED_ORIGINS=${SITE_URL:-https://opencodehub.example.com} |
79 | 77 | - MAX_REQUEST_BODY_MB=${MAX_REQUEST_BODY_MB:-10} |
80 | - # SSH server (runs inside app container) | |
81 | - - GIT_SSH_PORT=2222 | |
82 | - - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
83 | 78 | volumes: |
84 | 79 | - repos-data:/data/repos |
85 | 80 | - storage-data:/data/storage |
| @@ -121,6 +116,51 @@ services: | ||
121 | 116 | memory: 512M |
122 | 117 | cpus: "0.5" |
123 | 118 | |
119 | + # ── SSH Git Server ───────────────────────────────────────── | |
120 | + ssh-git: | |
121 | + build: | |
122 | + context: . | |
123 | + dockerfile: Dockerfile | |
124 | + container_name: opencodehub-ssh | |
125 | + restart: unless-stopped | |
126 | + ports: | |
127 | + - "${SSH_PORT:-2222}:2222" # SSH Git (exposed directly) | |
128 | + environment: | |
129 | + - NODE_ENV=production | |
130 | + - PROCESS_TYPE=ssh | |
131 | + - DATABASE_DRIVER=postgres | |
132 | + - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} | |
133 | + - REDIS_URL=redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 | |
134 | + - JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env} | |
135 | + - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} | |
136 | + - GIT_REPOS_PATH=/data/repos | |
137 | + - REPOS_PATH=/data/repos | |
138 | + - GIT_SSH_PORT=2222 | |
139 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
140 | + volumes: | |
141 | + - repos-data:/data/repos | |
142 | + - storage-data:/data/storage | |
143 | + - ssh-data:/data/ssh | |
144 | + - cache-data:/data/cache | |
145 | + depends_on: | |
146 | + postgres: | |
147 | + condition: service_healthy | |
148 | + redis: | |
149 | + condition: service_healthy | |
150 | + networks: | |
151 | + - opencodehub-internal | |
152 | + - opencodehub-external | |
153 | + logging: | |
154 | + driver: json-file | |
155 | + options: | |
156 | + max-size: "50m" | |
157 | + max-file: "5" | |
158 | + deploy: | |
159 | + resources: | |
160 | + limits: | |
161 | + memory: 512M | |
162 | + cpus: "0.5" | |
163 | + | |
124 | 164 | # ── Background Worker ────────────────────────────────────── |
125 | 165 | worker: |
126 | 166 | build: |
| @@ -128,16 +168,22 @@ services: | ||
128 | 168 | dockerfile: Dockerfile |
129 | 169 | container_name: opencodehub-worker |
130 | 170 | restart: unless-stopped |
131 | - command: ["bun", "scripts/worker.ts"] | |
132 | 171 | expose: |
133 | 172 | - "9090" # Worker health check (internal only) |
134 | 173 | environment: |
135 | 174 | - NODE_ENV=production |
175 | + - PROCESS_TYPE=worker | |
136 | 176 | - DATABASE_DRIVER=postgres |
137 | 177 | - DATABASE_URL=postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-opencodehub} |
138 | 178 | - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379 |
139 | 179 | - JWT_SECRET=${JWT_SECRET} |
180 | + - INTERNAL_HOOK_SECRET=${INTERNAL_HOOK_SECRET} | |
181 | + - GIT_REPOS_PATH=/data/repos | |
182 | + - REPOS_PATH=/data/repos | |
183 | + - STORAGE_TYPE=${STORAGE_TYPE:-local} | |
184 | + - STORAGE_PATH=/data/storage | |
140 | 185 | - LOG_LEVEL=${LOG_LEVEL:-info} |
186 | + - WORKER_HEALTH_PORT=9090 | |
141 | 187 | volumes: |
142 | 188 | - repos-data:/data/repos |
143 | 189 | - storage-data:/data/storage |
| @@ -304,7 +350,7 @@ services: | ||
304 | 350 | command: server /data --console-address ":9001" |
305 | 351 | environment: |
306 | 352 | MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} |
307 | - MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD in .env} | |
353 | + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} | |
308 | 354 | volumes: |
309 | 355 | - minio-data:/data |
310 | 356 | expose: |
311 | 357 | |
▼docker-compose.yml
+210-50
| @@ -1,108 +1,268 @@ | ||
1 | +# OpenCodeHub — self-hosted quickstart stack (production-safe) | |
2 | +# ============================================================ | |
3 | +# 1. cp .env.example .env | |
4 | +# 2. Fill in ALL required secrets in .env | |
5 | +# 3. docker compose up -d | |
6 | +# | |
7 | +# REQUIRED .env values (compose refuses to start without them): | |
8 | +# JWT_SECRET SESSION_SECRET INTERNAL_HOOK_SECRET CRON_SECRET | |
9 | +# RUNNER_SECRET AI_CONFIG_ENCRYPTION_KEY WORKFLOW_SECRET_ENCRYPTION_KEY | |
10 | +# POSTGRES_PASSWORD REDIS_PASSWORD | |
11 | +# → generate each with: openssl rand -hex 32 | |
12 | +# | |
13 | +# Storage — two backends only: | |
14 | +# - local (default): filesystem; point STORAGE_PATH/GIT_REPOS_PATH at a | |
15 | +# dedicated disk or NAS mount for best performance | |
16 | +# - s3: any S3-compatible store (AWS S3, MinIO, R2, Garage, ...) via | |
17 | +# STORAGE_ENDPOINT/STORAGE_BUCKET/STORAGE_ACCESS_KEY_ID/STORAGE_SECRET_ACCESS_KEY | |
18 | +# | |
19 | +# Optional profiles: | |
20 | +# docker compose --profile with-runner up -d # CI/CD runner (needs Docker-in-Docker) | |
21 | +# docker compose --profile with-minio up -d # local S3-compatible storage | |
22 | +# ============================================================ | |
23 | + | |
1 | 24 | services: |
2 | - # Main OpenCodeHub application | |
25 | + # ── Main OpenCodeHub application (web + API + Git HTTP) ──── | |
3 | 26 | app: |
4 | 27 | build: |
5 | 28 | context: . |
6 | 29 | dockerfile: Dockerfile |
7 | - container_name: opencodehub | |
30 | + container_name: opencodehub-app | |
8 | 31 | restart: unless-stopped |
32 | + env_file: .env | |
33 | + environment: | |
34 | + NODE_ENV: production | |
35 | + PORT: 4321 | |
36 | + PROCESS_TYPE: app | |
37 | + DATABASE_DRIVER: postgres | |
38 | + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} | |
39 | + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 | |
40 | + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} | |
41 | + SESSION_SECRET: ${SESSION_SECRET:?Set SESSION_SECRET in .env} | |
42 | + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} | |
43 | + CRON_SECRET: ${CRON_SECRET:?Set CRON_SECRET in .env} | |
44 | + RUNNER_SECRET: ${RUNNER_SECRET:?Set RUNNER_SECRET in .env} | |
45 | + AI_CONFIG_ENCRYPTION_KEY: ${AI_CONFIG_ENCRYPTION_KEY:?Set AI_CONFIG_ENCRYPTION_KEY in .env} | |
46 | + WORKFLOW_SECRET_ENCRYPTION_KEY: ${WORKFLOW_SECRET_ENCRYPTION_KEY:?Set WORKFLOW_SECRET_ENCRYPTION_KEY in .env} | |
47 | + SITE_URL: ${SITE_URL:-http://localhost:4321} | |
48 | + ALLOWED_ORIGINS: ${SITE_URL:-http://localhost:4321} | |
49 | + GIT_REPOS_PATH: /data/repos | |
50 | + REPOS_PATH: /data/repos | |
51 | + GIT_SSH_PORT: 2222 | |
52 | + GIT_SSH_HOST_KEY: /data/ssh/host_key | |
53 | + STORAGE_TYPE: ${STORAGE_TYPE:-local} | |
54 | + STORAGE_PATH: /data/storage | |
55 | + RATE_LIMIT_ENABLED: "true" | |
56 | + RATE_LIMIT_SKIP_DEV: "false" | |
57 | + CSRF_SKIP_DEV: "false" | |
9 | 58 | ports: |
10 | - - "4321:4321" # Web interface | |
11 | - - "2222:2222" # SSH Git | |
59 | + - "${WEB_PORT:-4321}:4321" # Web UI + API + Git HTTP | |
60 | + volumes: | |
61 | + - repos-data:/data/repos | |
62 | + - storage-data:/data/storage | |
63 | + - ssh-data:/data/ssh | |
64 | + - cache-data:/data/cache | |
65 | + depends_on: | |
66 | + postgres: | |
67 | + condition: service_healthy | |
68 | + redis: | |
69 | + condition: service_healthy | |
70 | + networks: | |
71 | + - och | |
72 | + healthcheck: | |
73 | + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"] | |
74 | + interval: 30s | |
75 | + timeout: 10s | |
76 | + retries: 3 | |
77 | + start_period: 20s | |
78 | + | |
79 | + # ─ ─ SSH Git server ───────────────────────────────────────── | |
80 | + ssh-git: | |
81 | + build: | |
82 | + context: . | |
83 | + dockerfile: Dockerfile | |
84 | + container_name: opencodehub-ssh | |
85 | + restart: unless-stopped | |
12 | 86 | environment: |
13 | - - NODE_ENV=production | |
14 | - - DATABASE_URL=postgresql://opencodehub:opencodehub@postgres:5432/opencodehub | |
15 | - - DATABASE_TYPE=postgres | |
16 | - - REDIS_URL=redis://redis:6379 | |
17 | - - JWT_SECRET=${JWT_SECRET:-supersecretjwtkey} | |
18 | - - WORKFLOW_SECRET_ENCRYPTION_KEY=${WORKFLOW_SECRET_ENCRYPTION_KEY:-0123456789abcdef0123456789abcdef} | |
19 | - - SERVER_URL=http://localhost:4321 | |
20 | - - DATA_DIR=/data | |
21 | - - STORAGE_TYPE=local | |
87 | + NODE_ENV: production | |
88 | + PROCESS_TYPE: ssh | |
89 | + DATABASE_DRIVER: postgres | |
90 | + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} | |
91 | + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 | |
92 | + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} | |
93 | + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} | |
94 | + GIT_REPOS_PATH: /data/repos | |
95 | + REPOS_PATH: /data/repos | |
96 | + GIT_SSH_PORT: 2222 | |
97 | + GIT_SSH_HOST_KEY: /data/ssh/host_key | |
98 | + ports: | |
99 | + - "${SSH_PORT:-2222}:2222" # SSH Git | |
22 | 100 | volumes: |
23 | - - ./data/opencodehub:/data | |
101 | + - repos-data:/data/repos | |
102 | + - storage-data:/data/storage | |
103 | + - ssh-data:/data/ssh | |
104 | + - cache-data:/data/cache | |
24 | 105 | depends_on: |
25 | 106 | postgres: |
26 | 107 | condition: service_healthy |
27 | 108 | redis: |
28 | 109 | condition: service_healthy |
29 | 110 | networks: |
30 | - - opencodehub-network | |
111 | + - och | |
112 | + | |
113 | + # ── Background worker (merge queue, webhooks, mirrors, digests) ── | |
114 | + worker: | |
115 | + build: | |
116 | + context: . | |
117 | + dockerfile: Dockerfile | |
118 | + container_name: opencodehub-worker | |
119 | + restart: unless-stopped | |
120 | + environment: | |
121 | + NODE_ENV: production | |
122 | + PROCESS_TYPE: worker | |
123 | + DATABASE_DRIVER: postgres | |
124 | + DATABASE_URL: postgresql://${POSTGRES_USER:-opencodehub}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-opencodehub} | |
125 | + REDIS_URL: redis://:${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env}@redis:6379 | |
126 | + JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} | |
127 | + INTERNAL_HOOK_SECRET: ${INTERNAL_HOOK_SECRET:?Set INTERNAL_HOOK_SECRET in .env} | |
128 | + GIT_REPOS_PATH: /data/repos | |
129 | + REPOS_PATH: /data/repos | |
130 | + STORAGE_TYPE: ${STORAGE_TYPE:-local} | |
131 | + STORAGE_PATH: /data/storage | |
132 | + WORKER_HEALTH_PORT: 9090 | |
133 | + expose: | |
134 | + - "9090" # Worker healthz/ready | |
135 | + volumes: | |
136 | + - repos-data:/data/repos | |
137 | + - storage-data:/data/storage | |
138 | + - ssh-data:/data/ssh | |
139 | + - cache-data:/data/cache | |
140 | + depends_on: | |
141 | + postgres: | |
142 | + condition: service_healthy | |
143 | + redis: | |
144 | + condition: service_healthy | |
145 | + networks: | |
146 | + - och | |
31 | 147 | healthcheck: |
32 | - test: | |
33 | - [ | |
34 | - "CMD", | |
35 | - "wget", | |
36 | - "--no-verbose", | |
37 | - "--tries=1", | |
38 | - "--spider", | |
39 | - "http://localhost:4321/api/health", | |
40 | - ] | |
148 | + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/healthz"] | |
41 | 149 | interval: 30s |
42 | 150 | timeout: 10s |
43 | 151 | retries: 3 |
44 | - start_period: 10s | |
152 | + start_period: 20s | |
45 | 153 | |
46 | - # PostgreSQL database | |
154 | + # ── PostgreSQL ────────────────────────────── ─────────────── | |
47 | 155 | postgres: |
48 | 156 | image: docker.io/library/postgres:16-alpine |
49 | 157 | container_name: opencodehub-postgres |
50 | 158 | restart: unless-stopped |
51 | 159 | environment: |
52 | - POSTGRES_USER: opencodehub | |
53 | - POSTGRES_PASSWORD: opencodehub | |
54 | - POSTGRES_DB: opencodehub | |
160 | + POSTGRES_USER: ${POSTGRES_USER:-opencodehub} | |
161 | + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} | |
162 | + POSTGRES_DB: ${POSTGRES_DB:-opencodehub} | |
55 | 163 | volumes: |
56 | - - ./data/postgres:/var/lib/postgresql/data | |
57 | - ports: | |
58 | - - "5432:5432" | |
164 | + - postgres-data:/var/lib/postgresql/data | |
59 | 165 | networks: |
60 | - - opencodehub-network | |
166 | + - och | |
61 | 167 | healthcheck: |
62 | - test: ["CMD-SHELL", "pg_isready -U opencodehub -d opencodehub"] | |
168 | + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-opencodehub} -d ${POSTGRES_DB:-opencodehub}"] | |
63 | 169 | interval: 10s |
64 | 170 | timeout: 5s |
65 | 171 | retries: 5 |
66 | 172 | |
67 | - # Redis for caching and sessions | |
173 | + # ── Redis (sessions, caching, distributed locking, queues) ── | |
68 | 174 | redis: |
69 | 175 | image: docker.io/library/redis:7-alpine |
70 | 176 | container_name: opencodehub-redis |
71 | 177 | restart: unless-stopped |
72 | - command: redis-server --appendonly yes | |
178 | + command: | |
179 | + - redis-server | |
180 | + - --requirepass | |
181 | + - ${REDIS_PASSWORD:?Set REDIS_PASSWORD in .env} | |
182 | + - --appendonly | |
183 | + - "yes" | |
184 | + - --maxmemory | |
185 | + - 256mb | |
186 | + - --maxmemory-policy | |
187 | + - allkeys-lru | |
73 | 188 | volumes: |
74 | - - ./data/redis:/data | |
75 | - ports: | |
76 | - - "6379:6379" | |
189 | + - redis-data:/data | |
77 | 190 | networks: |
78 | - - opencodehub-network | |
191 | + - och | |
79 | 192 | healthcheck: |
80 | - test: ["CMD", "redis-cli", "ping"] | |
193 | + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] | |
81 | 194 | interval: 10s |
82 | 195 | timeout: 5s |
83 | 196 | retries: 5 |
84 | 197 | |
85 | - # Pipeline runner (for CI/CD jobs) | |
198 | + # ── CI/CD Runner (Docker-in-Docker, optional) ────────────── | |
86 | 199 | runner: |
87 | 200 | build: |
88 | 201 | context: . |
89 | 202 | dockerfile: Dockerfile.runner |
90 | 203 | container_name: opencodehub-runner |
91 | 204 | restart: unless-stopped |
92 | - privileged: true # Required for Docker-in-Docker | |
205 | + privileged: true # Required for Docker-in-Docker | |
93 | 206 | environment: |
94 | - - RUNNER_TOKEN=${RUNNER_TOKEN:-} | |
95 | - - SERVER_URL=http://app:4321 | |
96 | - - DOCKER_HOST=unix:///var/run/docker.sock | |
207 | + RUNNER_TOKEN: ${RUNNER_TOKEN:-} | |
208 | + SERVER_URL: http://app:4321 | |
209 | + DOCKER_HOST: unix:///var/run/docker.sock | |
210 | + RUNNER_WORK_DIR: /work | |
211 | + RUNNER_CACHE_DIR: /cache | |
97 | 212 | volumes: |
98 | 213 | - /var/run/docker.sock:/var/run/docker.sock |
99 | - - ./data/runner/work:/work | |
100 | - - ./data/runner/cache:/cache | |
214 | + - runner-work:/work | |
215 | + - runner-cache:/cache | |
101 | 216 | depends_on: |
102 | 217 | - app |
103 | 218 | networks: |
104 | - - opencodehub-network | |
219 | + - och | |
220 | + profiles: | |
221 | + - with-runner | |
222 | + | |
223 | + # ── MinIO (S3-compatible storage, optional) ──────────────── | |
224 | + minio: | |
225 | + image: minio/minio:latest | |
226 | + container_name: opencodehub-minio | |
227 | + restart: unless-stopped | |
228 | + command: server /data --console-address ":9001" | |
229 | + environment: | |
230 | + MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} | |
231 | + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} | |
232 | + ports: | |
233 | + - "${MINIO_CONSOLE_PORT:-9001}:9001" # Web console (set 0 to disable) | |
234 | + volumes: | |
235 | + - minio-data:/data | |
236 | + networks: | |
237 | + - och | |
238 | + healthcheck: | |
239 | + test: ["CMD", "mc", "ready", "local"] | |
240 | + interval: 30s | |
241 | + timeout: 10s | |
242 | + retries: 3 | |
243 | + profiles: | |
244 | + - with-minio | |
245 | + | |
246 | +volumes: | |
247 | + repos-data: | |
248 | + driver: local | |
249 | + storage-data: | |
250 | + driver: local | |
251 | + ssh-data: | |
252 | + driver: local | |
253 | + cache-data: | |
254 | + driver: local | |
255 | + postgres-data: | |
256 | + driver: local | |
257 | + redis-data: | |
258 | + driver: local | |
259 | + runner-work: | |
260 | + driver: local | |
261 | + runner-cache: | |
262 | + driver: local | |
263 | + minio-data: | |
264 | + driver: local | |
105 | 265 | |
106 | 266 | networks: |
107 | - opencodehub-network: | |
267 | + och: | |
108 | 268 | driver: bridge |
109 | 269 | |
▼docker-entrypoint.sh
+37-10
| @@ -5,6 +5,17 @@ echo "╔═══════════════════════ | ||
5 | 5 | echo "║ OpenCodeHub Starting... ║" |
6 | 6 | echo "╚══════════════════════════════════════════╝" |
7 | 7 | |
8 | +# ── Process type ────────────────────────────────────────────── | |
9 | +# app (default) — web UI + API + Git HTTP | |
10 | +# ssh — SSH git server (scripts/ssh-server.ts) | |
11 | +# worker — background jobs (scripts/worker.ts) | |
12 | +PROCESS_TYPE="${PROCESS_TYPE:-app}" | |
13 | + | |
14 | +# ── Create required directories ────────────────────────────── | |
15 | +mkdir -p "${GIT_REPOS_PATH:-/data/repos}" "${STORAGE_PATH:-/data/storage}" "${CACHE_PATH:-/data/cache}" | |
16 | +mkdir -p "$(dirname "${LOG_FILE:-/data/logs/opencodehub.log}")" | |
17 | +mkdir -p "${SSH_PATH:-/data/ssh}" | |
18 | + | |
8 | 19 | # ── Wait for database ──────────────────────────────────────── |
9 | 20 | if [ -n "$DATABASE_URL" ]; then |
10 | 21 | echo "⏳ Waiting for database..." |
| @@ -23,23 +34,39 @@ if [ -n "$DATABASE_URL" ]; then | ||
23 | 34 | sleep 1 |
24 | 35 | done |
25 | 36 | |
26 | - # Run database migrations | |
37 | + # Run database migrations (deterministic — applies committed migrations in drizzle/) | |
27 | 38 | echo "🔄 Running database migrations..." |
28 | 39 | cd /app |
29 | - bunx drizzle-kit push --force 2>/dev/null || echo "⚠️ Migration push skipped (may already be up to date)" | |
40 | + if [ -d "/app/drizzle" ]; then | |
41 | + bun scripts/migrate.ts | |
42 | + else | |
43 | + echo "⚠️ No drizzle/ migrations found — skipping (use 'npm run db:push' in dev)" | |
44 | + fi | |
30 | 45 | echo "✅ Migrations complete" |
31 | 46 | fi |
32 | 47 | |
33 | 48 | # ── Initialize SSH host key ────────────────────────────────── |
34 | -if [ -n "$SSH_HOST_KEY_PATH" ] && [ ! -f "$SSH_HOST_KEY_PATH" ]; then | |
49 | +HOST_KEY="${GIT_SSH_HOST_KEY:-${SSH_HOST_KEY_PATH:-/data/ssh/host_key}}" | |
50 | +if [ -n "$HOST_KEY" ] && [ ! -f "$HOST_KEY" ]; then | |
35 | 51 | echo "🔑 Generating SSH host key..." |
36 | - ssh-keygen -t ed25519 -f "$SSH_HOST_KEY_PATH" -N "" -q | |
52 | + mkdir -p "$(dirname "$HOST_KEY")" | |
53 | + ssh-keygen -t ed25519 -f "$HOST_KEY" -N "" -q | |
37 | 54 | echo "✅ SSH host key generated" |
38 | 55 | fi |
39 | 56 | |
40 | -# ── Create required directories ───────────────────────────── ─ | |
41 | -mkdir -p "${REPOS_PATH:-/data/repos}" "${STORAGE_PATH:-/data/storage}" "${CACHE_PATH:-/data/cache}" | |
42 | -mkdir -p "$(dirname "${LOG_FILE:-/data/logs/opencodehub.log}")" | |
43 | - | |
44 | -echo "🚀 Starting OpenCodeHub on port ${PORT:-4321}..." | |
45 | -exec bun ./dist/server/entry.mjs | |
57 | +# ── Start the requested process ────────────────────────────── | |
58 | +cd /app | |
59 | +case "$PROCESS_TYPE" in | |
60 | + ssh) | |
61 | + echo "🚀 Starting OpenCodeHub SSH Git server on port ${GIT_SSH_PORT:-2222}..." | |
62 | + exec bun scripts/ssh-server.ts | |
63 | + ;; | |
64 | + worker) | |
65 | + echo "🚀 Starting OpenCodeHub background worker..." | |
66 | + exec bun scripts/worker.ts | |
67 | + ;; | |
68 | + *) | |
69 | + echo "🚀 Starting OpenCodeHub on port ${PORT:-4321}..." | |
70 | + exec bun ./dist/server/entry.mjs | |
71 | + ;; | |
72 | +esac | |
46 | 73 | |
▼docs-site/README.md
+42-35
| @@ -1,49 +1,56 @@ | ||
1 | -# Starlight Starter Kit: Basics | |
1 | +# OpenCodeHub Documentation Site | |
2 | 2 | |
3 | -[](https://starlight.astro.build) | |
3 | +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). | |
4 | 4 | |
5 | -``` | |
6 | -bun create astro@latest -- --template starlight | |
7 | -``` | |
5 | +**Live site:** <https://docs.opencodehub.space> | |
8 | 6 | |
9 | -> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun! | |
7 | +## Project Structure | |
10 | 8 | |
11 | -## 🚀 Project Structure | |
12 | 9 | |
13 | -Inside of your Astro + Starlight project, you'll see the following folders and files: | |
14 | 10 | |
15 | -``` | |
16 | -. | |
17 | -├── public/ | |
18 | -├── src/ | |
19 | -│ ├── assets/ | |
20 | -│ ├── content/ | |
21 | -│ │ └── docs/ | |
22 | -│ └── content.config.ts | |
23 | -├── astro.config.mjs | |
24 | -├── package.json | |
25 | -└── tsconfig.json | |
26 | -``` | |
11 | +Starlight looks for `.md` or `.mdx` files in `src/content/docs/`. Each file is exposed as a route based on its file name. | |
12 | + | |
13 | +Images go in `src/assets/` and are embedded in Markdown with relative links. Static assets like favicons go in `public/`. | |
14 | + | |
15 | +## Commands | |
27 | 16 | |
28 | -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. | |
17 | +All commands run from the project root: | |
29 | 18 | |
30 | -Images can be added to `src/assets/` and embedded in Markdown with a relative link. | |
19 | +| Command | Action | | |
20 | +| :-------------------- | :------------------------------------------------ | | |
21 | +| `bun install` | Install dependencies | | |
22 | +| `bun dev` | Start local dev server at `localhost:4321` | | |
23 | +| `bun build` | Build production site to `./dist/` | | |
24 | +| `bun preview` | Preview the production build locally | | |
25 | +| `bun deploy` | Deploy to Cloudflare Workers via Wrangler | | |
26 | +| `bun astro ...` | Run CLI commands like `astro add`, `astro check` | | |
27 | +| `bun astro -- --help` | Get help using the Astro CLI | | |
31 | 28 | |
32 | -Static assets, like favicons, can be placed in the `public/` directory. | |
29 | +## Development | |
33 | 30 | |
34 | -## 🧞 Commands | |
31 | +```bash | |
32 | +# Install dependencies | |
33 | +bun install | |
35 | 34 | |
36 | -All commands are run from the root of the project, from a terminal: | |
35 | +# Start dev server | |
36 | +bun dev | |
37 | +``` | |
38 | + | |
39 | +The dev server runs at `http://localhost:4321`. Edit files in `src/content/docs/` and the site hot-reloads. | |
40 | + | |
41 | +## Deployment | |
42 | + | |
43 | +This site is deployed to Cloudflare Workers. To deploy: | |
44 | + | |
45 | +```bash | |
46 | +bun build | |
47 | +bun deploy | |
48 | +``` | |
37 | 49 | |
38 | -| Command | Action | | |
39 | -| :------------------------ | :----------------------------------------------- | | |
40 | -| `bun install` | Installs dependencies | | |
41 | -| `bun dev` | Starts local dev server at `localhost:4321` | | |
42 | -| `bun build` | Build your production site to `./dist/` | | |
43 | -| `bun preview` | Preview your build locally, before deploying | | |
44 | -| `bun astro ...` | Run CLI commands like `astro add`, `astro check` | | |
45 | -| `bun astro -- --help` | Get help using the Astro CLI | | |
50 | +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. | |
46 | 51 | |
47 | -## 👀 Want to learn more? | |
52 | +## Tech Stack | |
48 | 53 | |
49 | -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). | |
54 | +- **Framework:** [Astro](https://astro.build) with [Starlight](https://starlight.astro.build) documentation theme | |
55 | +- **Deployment:** [Cloudflare Workers](https://workers.cloudflare.com) (static assets) | |
56 | +- **Package Manager:** [Bun](https://bun.sh) | |
50 | 57 | |
▼docs-site/astro.config.mjs
+36-3
| @@ -4,6 +4,7 @@ import { defineConfig } from "astro/config"; | ||
4 | 4 | |
5 | 5 | // https://astro.build/config |
6 | 6 | export default defineConfig({ |
7 | + site: "https://docs.opencodehub.space", | |
7 | 8 | integrations: [ |
8 | 9 | starlight({ |
9 | 10 | title: "OpenCodeHub Docs", |
| @@ -42,6 +43,7 @@ export default defineConfig({ | ||
42 | 43 | { label: "Branch Protection", slug: "guides/branch-protection" }, |
43 | 44 | { label: "Webhooks", slug: "guides/webhooks" }, |
44 | 45 | { label: "Storage Adapters", slug: "guides/storage-adapters" }, |
46 | + { label: "CircleCI Integration", slug: "guides/circleci-integration" }, | |
45 | 47 | ], |
46 | 48 | }, |
47 | 49 | { |
| @@ -65,11 +67,13 @@ export default defineConfig({ | ||
65 | 67 | { label: "Stacked PRs", slug: "features/stacked-prs" }, |
66 | 68 | { label: "AI Review", slug: "features/ai-review" }, |
67 | 69 | { label: "Merge Queue", slug: "features/merge-queue" }, |
70 | + { label: "CI/CD Actions", slug: "features/ci-actions" }, | |
71 | + { label: "CI/CD Pipelines", slug: "features/ci-cd" }, | |
68 | 72 | { label: "Automation Rules", slug: "features/automations" }, |
69 | 73 | { label: "PR Inbox", slug: "features/inbox" }, |
70 | 74 | { label: "Developer Metrics", slug: "features/developer-metrics" }, |
71 | 75 | { label: "Notifications", slug: "features/notifications" }, |
72 | - { label: "CI/CD Actions", slug: "features/ci-actions" }, | |
76 | + { label: "Slack Integration", slug: "features/slack-integration" }, | |
73 | 77 | { label: "CLI Workflows", slug: "features/cli" }, |
74 | 78 | ], |
75 | 79 | }, |
| @@ -86,6 +90,9 @@ export default defineConfig({ | ||
86 | 90 | { label: "cPanel", slug: "administration/deploy-cpanel" }, |
87 | 91 | { label: "CyberPanel", slug: "administration/deploy-cyberpanel" }, |
88 | 92 | { label: "Cloudflare", slug: "administration/deploy-cloudflare" }, |
93 | + { label: "Coolify", slug: "administration/deploy-coolify" }, | |
94 | + { label: "NAS (Synology/TrueNAS)", slug: "administration/deploy-nas" }, | |
95 | + { label: "Kubernetes (Helm)", slug: "administration/kubernetes" }, | |
89 | 96 | ], |
90 | 97 | }, |
91 | 98 | { |
| @@ -95,6 +102,19 @@ export default defineConfig({ | ||
95 | 102 | { label: "Configuration", slug: "administration/configuration" }, |
96 | 103 | { label: "Monitoring", slug: "administration/monitoring" }, |
97 | 104 | { label: "Security", slug: "administration/security" }, |
105 | + { label: "Deployment Matrix", slug: "administration/deployment-matrix" }, | |
106 | + { label: "Secret Rotation", slug: "administration/secret-rotation" }, | |
107 | + { label: "Expose NAS / Home Server", slug: "administration/expose-nas" }, | |
108 | + { label: "CircleCI Cloud Workers", slug: "administration/circleci-cloud" }, | |
109 | + { label: "CircleCI Self-Hosted", slug: "administration/circleci-worker" }, | |
110 | + ], | |
111 | + }, | |
112 | + { | |
113 | + label: "Operations", | |
114 | + items: [ | |
115 | + { label: "Incident Runbook", slug: "administration/incident-runbook" }, | |
116 | + { label: "Postmortem Template", slug: "administration/postmortem-template" }, | |
117 | + { label: "Operations Drills", slug: "administration/operations-drills" }, | |
98 | 118 | ], |
99 | 119 | }, |
100 | 120 | { |
| @@ -103,11 +123,24 @@ export default defineConfig({ | ||
103 | 123 | }, |
104 | 124 | { |
105 | 125 | label: "Development", |
106 | - autogenerate: { directory: "development" }, | |
126 | + items: [ | |
127 | + { label: "Architecture", slug: "development/architecture" }, | |
128 | + { label: "Local Dev Setup", slug: "development/local-dev-setup" }, | |
129 | + { label: "Contributing", slug: "development/contributing" }, | |
130 | + { label: "Database Schema", slug: "development/database-schema" }, | |
131 | + { label: "Database Migrations", slug: "development/database-migrations" }, | |
132 | + { label: "Testing", slug: "development/testing" }, | |
133 | + { label: "Testing & CI", slug: "development/testing-pipelines" }, | |
134 | + { label: "Security Hardening", slug: "development/security-hardening" }, | |
135 | + { label: "Observability", slug: "development/observability" }, | |
136 | + { label: "Publishing CLI", slug: "development/publishing-cli" }, | |
137 | + ], | |
107 | 138 | }, |
108 | 139 | { |
109 | 140 | label: "Reference", |
110 | - autogenerate: { directory: "reference" }, | |
141 | + items: [ | |
142 | + { label: "Glossary", slug: "reference/glossary" }, | |
143 | + ], | |
111 | 144 | }, |
112 | 145 | ], |
113 | 146 | }), |
114 | 147 | |
▼docs-site/src/content/docs/FREE-DEPLOYMENT.md
+399-0
| @@ -0,0 +1,399 @@ | ||
1 | +--- | |
2 | +title: "Free Deployment Options" | |
3 | +--- | |
4 | + | |
5 | +# OpenCodeHub — Free Deployment Options | |
6 | + | |
7 | +> Deploy OpenCodeHub for free with persistent file storage for git repositories. | |
8 | + | |
9 | +--- | |
10 | + | |
11 | +## Quick Comparison | |
12 | + | |
13 | +| Platform | Cost | Storage | RAM | CPU | Best For | | |
14 | +|----------|------|---------|-----|-----|----------| | |
15 | +| **Oracle Cloud Free** | $0 forever | 200GB | 24GB | 4 ARM cores | Best overall | | |
16 | +| **Home Server/NAS** | $0 | Unlimited | Your hardware | Your hardware | Privacy-focused | | |
17 | +| **Fly.io** | $0 (limited) | 3GB free | 256MB | Shared | Quick testing | | |
18 | +| **Google Cloud Free** | $0 forever | 30GB | 1GB | 0.25 vCPU | Light usage | | |
19 | +| **Railway** | $5 trial | 1GB | 512MB | Shared | Quick demo | | |
20 | +| **Hetzner** | ~€4/mo | 40GB | 4GB | 2 vCPU | Best value paid | | |
21 | + | |
22 | +--- | |
23 | + | |
24 | +## Option 1: Oracle Cloud Free Tier (Recommended) | |
25 | + | |
26 | +**Always Free** — No credit card charge, no time limit. | |
27 | + | |
28 | +### What You Get | |
29 | +- 4 ARM cores (Ampere A1) | |
30 | +- 24 GB RAM | |
31 | +- 200 GB boot volume | |
32 | +- 2 x 1TB block volumes (total 2TB) | |
33 | +- Free PostgreSQL database | |
34 | +- Free Redis | |
35 | + | |
36 | +### Steps | |
37 | + | |
38 | +```bash | |
39 | +# 1. Sign up at cloud.oracle.com | |
40 | +# 2. Create a VM instance: | |
41 | +# - Name: opencodehub | |
42 | +# - Image: Ubuntu 22.04 | |
43 | +# - Shape: VM.Standard.A1.Flex (4 OCPU, 24 GB RAM) | |
44 | +# - Upload SSH key | |
45 | +# - Create boot volume: 100 GB | |
46 | + | |
47 | +# 3. SSH into your instance | |
48 | +ssh -i ~/.ssh/key ubuntu@YOUR_PUBLIC_IP | |
49 | + | |
50 | +# 4. Install Docker | |
51 | +curl -fsSL https://get.docker.com -o get-docker.sh | |
52 | +sudo sh get-docker.sh | |
53 | +sudo usermod -aG docker $USER | |
54 | + | |
55 | +# 5. Install Docker Compose | |
56 | +sudo apt install docker-compose-plugin -y | |
57 | + | |
58 | +# 6. Clone OpenCodeHub | |
59 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
60 | +cd OpencodeHub | |
61 | + | |
62 | +# 7. Configure | |
63 | +cp .env.example .env | |
64 | +nano .env # Edit secrets | |
65 | + | |
66 | +# 8. Start | |
67 | +sudo docker compose up -d | |
68 | + | |
69 | +# 9. Create admin | |
70 | +sudo docker compose exec app bun run scripts/seed-admin.ts | |
71 | +``` | |
72 | + | |
73 | +### Access Your Instance | |
74 | +``` | |
75 | +Web: http://YOUR_PUBLIC_IP:4321 | |
76 | +SSH: ssh://git@YOUR_PUBLIC_IP:2222/owner/repo.git | |
77 | +HTTPS: Set up Nginx + Let's Encrypt (free) | |
78 | +``` | |
79 | + | |
80 | +--- | |
81 | + | |
82 | +## Option 2: Home Server / NAS | |
83 | + | |
84 | +**$0 forever** — Use hardware you already own. | |
85 | + | |
86 | +### What You Need | |
87 | +- Any computer running Linux/Mac/Windows | |
88 | +- 2GB+ RAM recommended | |
89 | +- 20GB+ free disk space | |
90 | + | |
91 | +### Quick Start (Linux/Mac) | |
92 | + | |
93 | +```bash | |
94 | +# Install Docker | |
95 | +curl -fsSL https://get.docker.com | sh | |
96 | + | |
97 | +# Clone and start | |
98 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
99 | +cd OpencodeHub | |
100 | +cp .env.example .env | |
101 | +nano .env # Set secrets | |
102 | +docker compose up -d | |
103 | + | |
104 | +# Create admin | |
105 | +docker compose exec app bun run scripts/seed-admin.ts | |
106 | +``` | |
107 | + | |
108 | +### NAS Deployment | |
109 | + | |
110 | +**Synology / TrueNAS / QNAP:** | |
111 | +1. Install Docker from Package Center | |
112 | +2. Use Docker Compose or Portainer | |
113 | +3. Mount a shared folder for `/data` | |
114 | + | |
115 | +### Access from Anywhere | |
116 | +- **Tailscale** (free): `tailscale up` → access via Tailscale IP | |
117 | +- **Cloudflare Tunnel** (free): Expose without opening ports | |
118 | +- **Port forwarding**: Open port 4321 on your router | |
119 | + | |
120 | +--- | |
121 | + | |
122 | +## Option 3: Fly.io Free Tier | |
123 | + | |
124 | +**Free** — 3GB persistent volume included. | |
125 | + | |
126 | +### Steps | |
127 | + | |
128 | +```bash | |
129 | +# 1. Install flyctl | |
130 | +curl -L https://fly.io/install.sh | sh | |
131 | + | |
132 | +# 2. Login | |
133 | +fly auth login | |
134 | + | |
135 | +# 3. Launch | |
136 | +fly launch --name opencodehub | |
137 | + | |
138 | +# 4. Add persistent volume | |
139 | +fly volume create opencodehub_data --region sjc --size 3 | |
140 | + | |
141 | +# 5. Set secrets | |
142 | +fly secrets set JWT_SECRET=$(openssl rand -hex 32) | |
143 | +fly secrets set SESSION_SECRET=$(openssl rand -hex 32) | |
144 | +fly secrets set DATABASE_URL="postgresql://..." | |
145 | +fly secrets set REDIS_URL="redis://..." | |
146 | + | |
147 | +# 6. Deploy | |
148 | +fly deploy | |
149 | +``` | |
150 | + | |
151 | +### Limitations | |
152 | +- 3GB storage (enough for small repos) | |
153 | +- 256MB RAM (may be slow for large repos) | |
154 | +- Apps休眠 after inactivity (cold starts take ~30s) | |
155 | + | |
156 | +--- | |
157 | + | |
158 | +## Option 4: Google Cloud Free Tier | |
159 | + | |
160 | +**Always Free** — e2-micro instance. | |
161 | + | |
162 | +### Steps | |
163 | + | |
164 | +```bash | |
165 | +# 1. Sign up at cloud.google.com | |
166 | +# 2. Create e2-micro VM: | |
167 | +# - Machine type: e2-micro (0.25 vCPU, 1GB RAM) | |
168 | +# - Boot disk: 30GB SSD (free) | |
169 | +# - Firewall: Allow HTTP (80), SSH (22) | |
170 | + | |
171 | +# 3. SSH in and install Docker | |
172 | +ssh -i ~/.ssh/key USER@EXTERNAL_IP | |
173 | +sudo apt update && sudo apt install docker.io docker-compose-plugin -y | |
174 | + | |
175 | +# 4. Deploy OpenCodeHub | |
176 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
177 | +cd OpencodeHub | |
178 | +cp .env.example .env | |
179 | +nano .env | |
180 | +docker compose up -d | |
181 | +``` | |
182 | + | |
183 | +### Limitations | |
184 | +- Only 1GB RAM (tight for production) | |
185 | +- 0.25 vCPU (slow builds) | |
186 | +- Good for personal/light use | |
187 | + | |
188 | +--- | |
189 | + | |
190 | +## Option 5: Self-Hosted on Existing VPS | |
191 | + | |
192 | +If you already have a VPS (Hetzner, DigitalOcean, Linode, etc.): | |
193 | + | |
194 | +```bash | |
195 | +# SSH into your server | |
196 | +ssh root@YOUR_SERVER_IP | |
197 | + | |
198 | +# One-line install | |
199 | +curl -fsSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash | |
200 | +``` | |
201 | + | |
202 | +--- | |
203 | + | |
204 | +## Option 6: Docker on Windows/Mac | |
205 | + | |
206 | +For local development or personal use: | |
207 | + | |
208 | +```bash | |
209 | +# Install Docker Desktop for Windows/Mac | |
210 | +# https://docs.docker.com/get-docker/ | |
211 | + | |
212 | +# Clone and start | |
213 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
214 | +cd OpencodeHub | |
215 | +cp .env.example .env | |
216 | +nano .env | |
217 | +docker compose up -d | |
218 | + | |
219 | +# Create admin | |
220 | +docker compose exec app bun run scripts/seed-admin.ts | |
221 | +``` | |
222 | + | |
223 | +--- | |
224 | + | |
225 | +## Storage Configuration | |
226 | + | |
227 | +### Local Storage (Default) | |
228 | +```bash | |
229 | +STORAGE_TYPE=local | |
230 | +STORAGE_PATH=/data/storage | |
231 | +GIT_REPOS_PATH=/data/repositories | |
232 | +``` | |
233 | + | |
234 | +### S3-Compatible (MinIO on same server) | |
235 | +```bash | |
236 | +# Add to docker-compose.yml: | |
237 | +minio: | |
238 | + image: minio/minio | |
239 | + command: server /data --console-address ":9001" | |
240 | + volumes: | |
241 | + - ./data/minio:/data | |
242 | + ports: | |
243 | + - "9001:9001" | |
244 | + | |
245 | +STORAGE_TYPE=s3 | |
246 | +STORAGE_ENDPOINT=http://minio:9000 | |
247 | +STORAGE_BUCKET=opencodehub | |
248 | +STORAGE_ACCESS_KEY_ID=minioadmin | |
249 | +STORAGE_SECRET_ACCESS_KEY=minioadmin | |
250 | +``` | |
251 | + | |
252 | +--- | |
253 | + | |
254 | +## HTTPS Setup (Free) | |
255 | + | |
256 | +### With Let's Encrypt + Nginx | |
257 | + | |
258 | +```bash | |
259 | +# Install Nginx and Certbot | |
260 | +sudo apt install nginx certbot python3-certbot-nginx -y | |
261 | + | |
262 | +# Create Nginx config | |
263 | +sudo nano /etc/nginx/sites-available/opencodehub | |
264 | +``` | |
265 | + | |
266 | +```nginx | |
267 | +server { | |
268 | + listen 80; | |
269 | + server_name git.yourdomain.com; | |
270 | + return 301 https://$host$request_uri; | |
271 | +} | |
272 | + | |
273 | +server { | |
274 | + listen 443 ssl http2; | |
275 | + server_name git.yourdomain.com; | |
276 | + | |
277 | + ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; | |
278 | + ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; | |
279 | + | |
280 | + client_max_body_size 500M; | |
281 | + | |
282 | + location / { | |
283 | + proxy_pass http://localhost:4321; | |
284 | + proxy_set_header Host $host; | |
285 | + proxy_set_header X-Real-IP $remote_addr; | |
286 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
287 | + proxy_set_header X-Forwarded-Proto $scheme; | |
288 | + } | |
289 | + | |
290 | + # WebSocket support | |
291 | + location /api/realtime { | |
292 | + proxy_pass http://localhost:4321; | |
293 | + proxy_http_version 1.1; | |
294 | + proxy_set_header Upgrade $http_upgrade; | |
295 | + proxy_set_header Connection "upgrade"; | |
296 | + } | |
297 | +} | |
298 | +``` | |
299 | + | |
300 | +```bash | |
301 | +# Enable and get SSL | |
302 | +sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/ | |
303 | +sudo certbot --nginx -d git.yourdomain.com | |
304 | +``` | |
305 | + | |
306 | +--- | |
307 | + | |
308 | +## Environment Variables (Production) | |
309 | + | |
310 | +```bash | |
311 | +# Generate all secrets | |
312 | +JWT_SECRET=$(openssl rand -hex 32) | |
313 | +SESSION_SECRET=$(openssl rand -hex 32) | |
314 | +INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) | |
315 | +CRON_SECRET=$(openssl rand -hex 32) | |
316 | +RUNNER_SECRET=$(openssl rand -hex 32) | |
317 | +AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) | |
318 | +WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) | |
319 | + | |
320 | +# Your .env file | |
321 | +DATABASE_DRIVER=postgres | |
322 | +DATABASE_URL=postgresql://opencodehub:PASSWORD@localhost:5432/opencodehub | |
323 | +DATABASE_SSL=false | |
324 | +REDIS_URL=redis://localhost:6379 | |
325 | +SITE_URL=https://git.yourdomain.com | |
326 | +STORAGE_TYPE=local | |
327 | +GIT_REPOS_PATH=/data/repositories | |
328 | +NODE_ENV=production | |
329 | +``` | |
330 | + | |
331 | +--- | |
332 | + | |
333 | +## Backup Strategy | |
334 | + | |
335 | +### Automated Backup Script | |
336 | +```bash | |
337 | +#!/bin/bash | |
338 | +# backup.sh — Run daily via cron | |
339 | +BACKUP_DIR="/backups/$(date +%Y-%m-%d)" | |
340 | +mkdir -p "$BACKUP_DIR" | |
341 | + | |
342 | +# Database dump | |
343 | +docker compose exec -T postgres pg_dump -U opencodehub opencodehub > "$BACKUP_DIR/db.sql" | |
344 | + | |
345 | +# Git repos | |
346 | +tar czf "$BACKUP_DIR/repos.tar.gz" ./data/repositories/ | |
347 | + | |
348 | +# Upload to S3 (optional) | |
349 | +# aws s3 sync "$BACKUP_DIR" s3://your-backup-bucket/opencodehub/ | |
350 | +``` | |
351 | + | |
352 | +### Cron Job | |
353 | +```bash | |
354 | +# Add to crontab | |
355 | +crontab -e | |
356 | +0 2 * * * /path/to/backup.sh | |
357 | +``` | |
358 | + | |
359 | +--- | |
360 | + | |
361 | +## Monitoring | |
362 | + | |
363 | +### Health Check | |
364 | +```bash | |
365 | +# Check if app is running | |
366 | +curl -f http://localhost:4321/api/health || echo "DOWN" | |
367 | + | |
368 | +# Check Docker status | |
369 | +docker compose ps | |
370 | +docker compose logs --tail=50 app | |
371 | +``` | |
372 | + | |
373 | +### Uptime Robot (Free) | |
374 | +1. Sign up at uptimerobot.com | |
375 | +2. Add HTTP monitor: `http://YOUR_IP:4321/api/health` | |
376 | +3. Get notified if your instance goes down | |
377 | + | |
378 | +--- | |
379 | + | |
380 | +## Recommended Free Setup | |
381 | + | |
382 | +For a **personal Git server** that stores all your code: | |
383 | + | |
384 | +| Component | Choice | | |
385 | +|-----------|--------| | |
386 | +| **Cloud** | Oracle Cloud Free Tier | | |
387 | +| **Server** | ARM A1 (4 cores, 24GB RAM) | | |
388 | +| **Storage** | 100GB boot + 1TB block volume | | |
389 | +| **Database** | PostgreSQL (Docker) | | |
390 | +| **Cache** | Redis (Docker) | | |
391 | +| **HTTPS** | Nginx + Let's Encrypt | | |
392 | +| **Access** | Tailscale (private) or port forwarding | | |
393 | +| **Backups** | Cron script + local S3 | | |
394 | + | |
395 | +**Total cost: $0/month forever.** | |
396 | + | |
397 | +--- | |
398 | + | |
399 | +*Generated for OpenCodeHub free deployment.* | |
0 | 400 | |
▼docs-site/src/content/docs/RENDER-DEPLOYMENT.md
+167-0
| @@ -0,0 +1,167 @@ | ||
1 | +--- | |
2 | +title: "Render Deployment Guide" | |
3 | +--- | |
4 | + | |
5 | +# OpenCodeHub — Render Deployment Guide | |
6 | + | |
7 | +> Deploy to Render (Singapore) with free PostgreSQL + Upstash Redis. $0/month. | |
8 | + | |
9 | +--- | |
10 | + | |
11 | +## What You Get | |
12 | + | |
13 | +- OpenCodeHub running in Asia (Singapore) | |
14 | +- Free PostgreSQL database | |
15 | +- Free Redis via Upstash | |
16 | +- 1GB persistent storage for git repos | |
17 | +- HTTPS included | |
18 | +- Admin account | |
19 | + | |
20 | +--- | |
21 | + | |
22 | +## Step 1: Create Upstash Redis (2 min) | |
23 | + | |
24 | +1. Go to **https://upstash.com** → Sign up (free) | |
25 | +2. Click **Create Database** | |
26 | +3. Settings: | |
27 | + - **Name**: `opencodehub` | |
28 | + - **Region**: **Asia Pacific (Singapore)** | |
29 | + - **Plan**: Pay as you go (free tier: 10K commands/day) | |
30 | +4. Click **Create** | |
31 | +5. Copy the **Redis URL** (looks like `rediss://...@...`) | |
32 | +6. Keep this tab open — you'll need it in Step 3 | |
33 | + | |
34 | +--- | |
35 | + | |
36 | +## Step 2: Deploy to Render (5 min) | |
37 | + | |
38 | +1. Go to **https://render.com** → Sign up (free) | |
39 | +2. Click **New** → **Blueprint** | |
40 | +3. Connect your GitHub account | |
41 | +4. Select your OpenCodeHub repository | |
42 | +5. Render detects `render.yaml` and shows: | |
43 | + - `opencodehub-db` (PostgreSQL — Singapore) | |
44 | + - `opencodehub` (Web Service — Singapore) | |
45 | +6. Click **Apply** | |
46 | +7. Wait for deployment (~3-5 min first time) | |
47 | + | |
48 | +--- | |
49 | + | |
50 | +## Step 3: Set Redis URL (1 min) | |
51 | + | |
52 | +1. Go to your Web Service → **Environment** tab | |
53 | +2. Find `REDIS_URL` (shows "Not set") | |
54 | +3. Click the pencil icon → Paste your **Upstash Redis URL** | |
55 | +4. Click **Save** | |
56 | + | |
57 | +--- | |
58 | + | |
59 | +## Step 4: Set Site URL (1 min) | |
60 | + | |
61 | +1. In the same **Environment** tab | |
62 | +2. Find `SITE_URL` (shows "Not set") | |
63 | +3. Click the pencil icon → Enter your Render URL: | |
64 | + ``` | |
65 | + https://opencodehub.onrender.com | |
66 | + ``` | |
67 | + (Replace with your actual URL from the service overview) | |
68 | +4. Click **Save** | |
69 | + | |
70 | +--- | |
71 | + | |
72 | +## Step 5: Manual Deploy | |
73 | + | |
74 | +1. Go to your Web Service → **Manual Deploy** | |
75 | +2. Click **Deploy latest commit** | |
76 | +3. Wait for build to complete (~2-3 min) | |
77 | +4. Click the **URL** at the top to open your instance | |
78 | + | |
79 | +--- | |
80 | + | |
81 | +## Step 6: Create Admin Account | |
82 | + | |
83 | +You'll see the **Setup Wizard**. Fill in: | |
84 | + | |
85 | +| Field | Value | | |
86 | +|-------|-------| | |
87 | +| Username | `admin` (or your choice) | | |
88 | +| Email | Your email | | |
89 | +| Password | Strong password | | |
90 | + | |
91 | +Click **Create Account** — you're now logged in as admin! | |
92 | + | |
93 | +--- | |
94 | + | |
95 | +## Step 7: Test It | |
96 | + | |
97 | +```bash | |
98 | +# Create a repo via web UI, then clone: | |
99 | +git clone https://opencodehub.onrender.com/your-username/my-repo.git | |
100 | + | |
101 | +# Push code: | |
102 | +cd my-repo | |
103 | +echo "# Hello" > README.md | |
104 | +git add . && git commit -m "Initial" | |
105 | +git push origin main | |
106 | +``` | |
107 | + | |
108 | +--- | |
109 | + | |
110 | +## Done! Summary | |
111 | + | |
112 | +| Component | Service | Region | Cost | | |
113 | +|-----------|---------|--------|------| | |
114 | +| App | Render Free | Singapore | $0 | | |
115 | +| PostgreSQL | Render Free | Singapore | $0 | | |
116 | +| Redis | Upstash Free | Singapore | $0 | | |
117 | +| Storage | Render Disk 1GB | Singapore | $0 | | |
118 | +| **Total** | | | **$0/month** | | |
119 | + | |
120 | +--- | |
121 | + | |
122 | +## Keep Alive (Optional) | |
123 | + | |
124 | +Render free tier spins down after 15min inactivity. To prevent: | |
125 | + | |
126 | +1. Go to **https://uptimerobot.com** → Sign up (free) | |
127 | +2. Add Monitor → Type: HTTP(s) | |
128 | +3. URL: `https://opencodehub.onrender.com/api/health` | |
129 | +4. Monitoring interval: 5 minutes | |
130 | +5. Save — your app stays awake | |
131 | + | |
132 | +--- | |
133 | + | |
134 | +## Upgrade Path (When You Grow) | |
135 | + | |
136 | +| Need | Upgrade | Cost | | |
137 | +|------|---------|------| | |
138 | +| Always-on (no spin-down) | Render Starter | $7/mo | | |
139 | +| More storage (10GB+) | Render Disk upgrade | $1-7/mo | | |
140 | +| More Redis commands | Upstash Pay-as-you-go | ~$0.20/10K | | |
141 | +| PostgreSQL expiry | Render Starter DB | $7/mo | | |
142 | + | |
143 | +--- | |
144 | + | |
145 | +## Troubleshooting | |
146 | + | |
147 | +**App won't start?** | |
148 | +- Check **Logs** tab in Render | |
149 | +- Verify `REDIS_URL` is set correctly | |
150 | +- Ensure `SITE_URL` matches your Render URL | |
151 | + | |
152 | +**Redis connection failed?** | |
153 | +- Verify Redis URL from Upstash (starts with `rediss://`) | |
154 | +- Check Upstash dashboard shows "Active" | |
155 | +- Ensure region is Singapore | |
156 | + | |
157 | +**Git clone fails?** | |
158 | +- Use HTTPS: `git clone https://your-url.onrender.com/user/repo.git` | |
159 | +- SSH not available on Render free tier (use HTTPS) | |
160 | + | |
161 | +**Slow first request?** | |
162 | +- Normal for free tier (cold start ~30s) | |
163 | +- Add UptimeRobot ping to keep it warm | |
164 | + | |
165 | +--- | |
166 | + | |
167 | +*OpenCodeHub — Deployed on Render, Asia Region, $0/month.* | |
0 | 168 | |
▼docs-site/src/content/docs/administration/air-gapped.md
+33-0
| @@ -0,0 +1,33 @@ | ||
1 | +# Air-gapped Environments | |
2 | + | |
3 | +OpenCodeHub can be deployed in fully offline, air-gapped networks. | |
4 | + | |
5 | +## Prerequisites | |
6 | +- A local container registry to host the OpenCodeHub Docker image. | |
7 | +- A local NPM/mirror registry if you plan to build from source. | |
8 | + | |
9 | +## Deployment Steps | |
10 | +1. On a machine with internet access, pull the required images: | |
11 | + ```bash | |
12 | + docker pull ghcr.io/swadhinbiswas/opencodehub:latest | |
13 | + docker pull postgres:15-alpine | |
14 | + docker pull redis:7-alpine | |
15 | + ``` | |
16 | +2. Save the images to tar archives: | |
17 | + ```bash | |
18 | + docker save ghcr.io/swadhinbiswas/opencodehub:latest -o och.tar | |
19 | + docker save postgres:15-alpine -o pg.tar | |
20 | + docker save redis:7-alpine -o redis.tar | |
21 | + ``` | |
22 | +3. Transfer the archives to the air-gapped network via secure media. | |
23 | +4. Load the images on the target machine: | |
24 | + ```bash | |
25 | + docker load -i och.tar | |
26 | + docker load -i pg.tar | |
27 | + docker load -i redis.tar | |
28 | + ``` | |
29 | + | |
30 | +## Configuration Adjustments | |
31 | +- Disable external OAuth providers (GitHub, Google) and rely exclusively on local authentication, LDAP, or an internal SAML provider. | |
32 | +- Disable outbound webhooks pointing to external SaaS platforms. | |
33 | +- 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. | |
0 | 34 | |
▼docs-site/src/content/docs/administration/backup-restore.md
+29-0
| @@ -0,0 +1,29 @@ | ||
1 | +# Backup and Restore | |
2 | + | |
3 | +This guide covers how to back up and restore an OpenCodeHub instance. | |
4 | + | |
5 | +## Backup Strategy | |
6 | + | |
7 | +### 1. Database | |
8 | +For PostgreSQL, use `pg_dump`: | |
9 | +```bash | |
10 | +pg_dump -U opencodehub -d opencodehub_db > opencodehub_backup.sql | |
11 | +``` | |
12 | + | |
13 | +### 2. Git Repositories | |
14 | +The Git repositories are stored in the path defined by `GIT_REPOS_PATH` (default: `./data/repos`). | |
15 | +You can back this up using `rsync` or `tar`: | |
16 | +```bash | |
17 | +tar -czvf repos_backup.tar.gz ./data/repos/ | |
18 | +``` | |
19 | + | |
20 | +### 3. Object Storage | |
21 | +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. | |
22 | + | |
23 | +## Restore Strategy | |
24 | +To restore: | |
25 | +1. Stop the application and worker processes. | |
26 | +2. Restore the database using `psql`. | |
27 | +3. Extract the Git repositories to `GIT_REPOS_PATH`. | |
28 | +4. Restore object storage data. | |
29 | +5. Restart the application. | |
0 | 30 | |
▼docs-site/src/content/docs/administration/configuration.md
+187-36
| @@ -2,64 +2,215 @@ | ||
2 | 2 | title: "Configuration Reference" |
3 | 3 | --- |
4 | 4 | |
5 | +OpenCodeHub is configured via environment variables. Copy `.env.example` to `.env` and edit. | |
5 | 6 | |
6 | -OpenCodeHub is configured exclusively via environment variables. | |
7 | +## Server | |
7 | 8 | |
8 | -## Core | |
9 | +| Variable | Description | Default | | |
10 | +|----------|-------------|---------| | |
11 | +| `HOST` | Bind address | `0.0.0.0` | | |
12 | +| `PORT` | HTTP port | `4321` | | |
13 | +| `SITE_URL` | Public URL of the instance | `http://localhost:4321` | | |
14 | +| `APP_NAME` | Instance name | `OpenCodeHub` | | |
15 | +| `NODE_ENV` | `development` or `production` | `development` | | |
9 | 16 | |
10 | -| Variable | Description | Required | Default | | |
11 | -|----------|-------------|----------|---------| | |
12 | -| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:3000` | | |
13 | -| `PORT` | Port to listen on | No | `3000` | | |
14 | -| `NODE_ENV` | `development` or `production` | No | `development` | | |
17 | +## Database | |
15 | 18 | |
16 | -## Security (Critical) | |
19 | +| Variable | Description | Default | | |
20 | +|----------|-------------|---------| | |
21 | +| `DATABASE_DRIVER` | `postgres`, `sqlite`, or `turso` (LibSQL) | `postgres` | | |
22 | +| `DATABASE_URL` | Connection string | — | | |
23 | +| `DATABASE_POOL_SIZE` | Connection pool size | `10` | | |
24 | +| `DATABASE_SSL` | Enable PostgreSQL SSL | `false` | | |
25 | +| `DATABASE_AUTH_TOKEN` | Auth token (Turso/LibSQL only) | — | | |
26 | + | |
27 | +:::caution[Production] | |
28 | +PostgreSQL is **required** for production. All schemas use `pgTable`. SQLite is for development only. | |
29 | +::: | |
30 | + | |
31 | +## Redis | |
32 | + | |
33 | +| Variable | Description | Default | | |
34 | +|----------|-------------|---------| | |
35 | +| `REDIS_URL` | Redis connection string | `redis://localhost:6379` | | |
36 | +| `REDIS_PASSWORD` | Redis password | — | | |
37 | + | |
38 | +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`. | |
39 | + | |
40 | +## Authentication | |
17 | 41 | |
18 | 42 | | Variable | Description | Generate With | |
19 | 43 | |----------|-------------|---------------| |
20 | -| `JWT_SECRET` | Signs auth tokens | `openssl rand -hex 32` | | |
21 | -| `SESSION_SECRET` | Signs session cookies | `openssl rand -hex 32` | | |
22 | -| `INTERNAL_HOOK_SECRET` | Secures git hooks | `openssl rand -hex 32` | | |
44 | +| `JWT_SECRET` | Signs auth tokens | `openssl rand -base64 32` | | |
45 | +| `JWT_EXPIRES_IN` | Token expiry | `7d` | | |
46 | +| `SESSION_SECRET` | Signs session cookies | `openssl rand -base64 32` | | |
23 | 47 | |
24 | -## Database | |
48 | +## Security Secrets | |
25 | 49 | |
26 | -| Variable | Description | Example | | |
50 | +All secrets below must be unique. Generate each with `openssl rand -hex 32`. | |
51 | + | |
52 | +| Variable | Purpose | | |
53 | +|----------|---------| | |
54 | +| `INTERNAL_HOOK_SECRET` | Secures git hook communication between Git server and app | | |
55 | +| `CRON_SECRET` | Protects cron endpoints (GC, mirror sync, notification digests) | | |
56 | +| `AI_CONFIG_ENCRYPTION_KEY` | Encrypts AI provider API keys at rest | | |
57 | +| `WORKFLOW_SECRET_ENCRYPTION_KEY` | Encrypts CI/CD workflow secrets at rest | | |
58 | +| `RUNNER_SECRET` | Shared secret between server and CI runners | | |
59 | +| `METRICS_TOKEN` | Bearer token for `GET /api/metrics` (Prometheus). Empty = public | | |
60 | + | |
61 | +## OAuth Providers | |
62 | + | |
63 | +| Variable | Description | | |
64 | +|----------|-------------| | |
65 | +| `OAUTH_GOOGLE_CLIENT_ID` | Google OAuth client ID | | |
66 | +| `OAUTH_GOOGLE_CLIENT_SECRET` | Google OAuth client secret | | |
67 | +| `OAUTH_GITHUB_CLIENT_ID` | GitHub OAuth client ID | | |
68 | +| `OAUTH_GITHUB_CLIENT_SECRET` | GitHub OAuth client secret | | |
69 | +| `OAUTH_GITLAB_CLIENT_ID` | GitLab OAuth client ID | | |
70 | +| `OAUTH_GITLAB_CLIENT_SECRET` | GitLab OAuth client secret | | |
71 | + | |
72 | +OpenCodeHub also supports SSO via SAML and OIDC. Configure these through the admin dashboard. | |
73 | + | |
74 | +## Git Configuration | |
75 | + | |
76 | +| Variable | Description | Default | | |
27 | 77 | |----------|-------------|---------| |
28 | -| `DATABASE_DRIVER` | `postgres`, `mysql`, `sqlite`, `libsql` | `postgres` | | |
29 | -| `DATABASE_URL` | Connection string | `postgresql://user:pass@host:5432/db` | | |
30 | -| `DATABASE_AUTH_TOKEN`| Auth token (Turso/LibSQL only) | | | |
78 | +| `GIT_REPOS_PATH` | Directory for git repositories | `./data/repositories` | | |
79 | +| `GIT_SSH_PORT` | SSH git server port | `2222` | | |
80 | +| `GIT_SSH_HOST_KEY` | Path to SSH host key | `./data/ssh/host_key` | | |
31 | 81 | |
32 | 82 | ## Storage |
33 | 83 | |
34 | 84 | | Variable | Description | Default | |
35 | 85 | |----------|-------------|---------| |
36 | -| `STORAGE_TYPE` | `local` (filesystem) or `s3` (any S3-compatible object store) | `local` | | |
37 | -| `STORAGE_PATH` | Path for local storage | `./data/storage` | | |
38 | -| `STORAGE_BUCKET` | Bucket name (required for `s3`) | | | |
39 | -| `STORAGE_REGION` | S3 region | `us-east-1` | | |
40 | -| `STORAGE_ENDPOINT` | Custom S3 endpoint (MinIO, R2, Garage, SeaweedFS, Ceph RGW, etc.) | | | |
41 | -| `STORAGE_ACCESS_KEY_ID` | S3 access key | | | |
42 | -| `STORAGE_SECRET_ACCESS_KEY` | S3 secret key | | | |
86 | +| `STORAGE_TYPE` | `local` or `s3` | `local` | | |
87 | +| `STORAGE_PATH` | Local storage path | `./data/storage` | | |
43 | 88 | |
44 | -> Only `local` and `s3` storage backends are supported. Previous releases | |
45 | -> also supported `gdrive`, `azure`, `gcs`, `dropbox`, `onedrive`, `ftp`, and | |
46 | -> an rclone-as-adapter option; these have been removed in favour of | |
47 | -> S3-compatible object storage. See `docs/guides/storage-adapters.md`. | |
89 | +### S3-Compatible Storage | |
48 | 90 | |
49 | -## AI Review | |
91 | +Works with AWS S3, MinIO, Cloudflare R2, Garage, SeaweedFS, Ceph RGW, Wasabi, Backblaze B2, and any S3-compatible service. | |
50 | 92 | |
51 | 93 | | Variable | Description | |
52 | 94 | |----------|-------------| |
53 | -| `AI_PROVIDER` | `openai` or `anthropic` | | |
54 | -| `OPENAI_API_KEY` | OpenAI API Key | | |
55 | -| `ANTHROPIC_API_KEY` | Anthropic API Key | | |
95 | +| `STORAGE_BUCKET` | Bucket name (required for S3) | | |
96 | +| `STORAGE_REGION` | S3 region (default: `us-east-1`) | | |
97 | +| `STORAGE_ENDPOINT` | Custom S3 endpoint (for MinIO, R2, etc.) | | |
98 | +| `STORAGE_ACCESS_KEY_ID` | S3 access key | | |
99 | +| `STORAGE_SECRET_ACCESS_KEY` | S3 secret key | | |
100 | + | |
101 | +:::tip[Performance] | |
102 | +For best performance, use **local SSD storage**. Object storage (S3/R2) introduces latency. Use S3 for multi-node deployments or backup. | |
103 | +::: | |
104 | + | |
105 | +## CI/CD Runner | |
106 | + | |
107 | +| Variable | Description | Default | | |
108 | +|----------|-------------|---------| | |
109 | +| `RUNNER_ENABLED` | Enable the CI runner | `true` | | |
110 | +| `RUNNER_DOCKER_SOCKET` | Docker socket path | `/var/run/docker.sock` | | |
111 | +| `RUNNER_WORKSPACE_PATH` | Workspace directory | `./data/runner/workspaces` | | |
112 | +| `RUNNER_ARTIFACTS_PATH` | Artifacts directory | `./data/runner/artifacts` | | |
113 | +| `RUNNER_CONCURRENT_JOBS` | Max concurrent jobs | `2` | | |
114 | +| `RUNNER_TIMEOUT` | Job timeout in seconds | `3600` | | |
115 | +| `RUNNER_REGISTRATION_TOKEN_TTL_DAYS` | Token TTL | `90` | | |
56 | 116 | |
57 | 117 | ## Email (SMTP) |
58 | 118 | |
119 | +| Variable | Description | Default | | |
120 | +|----------|-------------|---------| | |
121 | +| `SMTP_HOST` | SMTP hostname | — | | |
122 | +| `SMTP_PORT` | SMTP port | `587` | | |
123 | +| `SMTP_USER` | SMTP username | — | | |
124 | +| `SMTP_PASSWORD` | SMTP password | — | | |
125 | +| `SMTP_FROM` | From address | `[email protected]` | | |
126 | + | |
127 | +## Logging | |
128 | + | |
129 | +| Variable | Description | Default | | |
130 | +|----------|-------------|---------| | |
131 | +| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` | `info` | | |
132 | +| `LOG_FILE` | Log file path | `./data/logs/opencodehub.log` | | |
133 | + | |
134 | +## Rate Limiting | |
135 | + | |
136 | +| Variable | Description | Default | | |
137 | +|----------|-------------|---------| | |
138 | +| `RATE_LIMIT_ENABLED` | Enable rate limiting | `true` | | |
139 | +| `RATE_LIMIT_AUTH` | Auth endpoints (req/15min) | `5` | | |
140 | +| `RATE_LIMIT_API` | API endpoints (req/min) | `100` | | |
141 | +| `RATE_LIMIT_GIT` | Git operations (req/min) | `200` | | |
142 | +| `RATE_LIMIT_GENERAL` | General endpoints (req/min) | `60` | | |
143 | +| `RATE_LIMIT_SKIP_DEV` | Skip in development | `false` | | |
144 | +| `CSRF_SKIP_DEV` | Skip CSRF in development | `false` | | |
145 | + | |
146 | +## CORS & Request Limits | |
147 | + | |
148 | +| Variable | Description | Default | | |
149 | +|----------|-------------|---------| | |
150 | +| `ALLOWED_ORIGINS` | Comma-separated allowed origins | `http://localhost:4321` | | |
151 | +| `MAX_REQUEST_BODY_MB` | Max request body size (MB) | `10` | | |
152 | +| `MAX_PACK_SIZE_MB` | Max git pack size (MB) | `500` | | |
153 | +| `GIT_PROCESS_TIMEOUT_SECS` | Git process timeout (sec) | `300` | | |
154 | + | |
155 | +## Feature Flags | |
156 | + | |
157 | +| Variable | Description | Default | | |
158 | +|----------|-------------|---------| | |
159 | +| `ENABLE_REGISTRATION` | Allow new user signups | `true` | | |
160 | +| `ENABLE_WIKI` | Enable repository wikis | `true` | | |
161 | +| `ENABLE_ISSUES` | Enable issue tracking | `true` | | |
162 | +| `ENABLE_ACTIONS` | Enable CI/CD pipelines | `true` | | |
163 | +| `ENABLE_PACKAGES` | Enable package registry | `false` | | |
164 | +| `ENABLE_AI_ASSISTANT` | Enable AI code review | `true` | | |
165 | + | |
166 | +## AI Providers | |
167 | + | |
168 | +Configure one or more AI providers for code review. Keys are encrypted at rest using `AI_CONFIG_ENCRYPTION_KEY`. | |
169 | + | |
170 | +| Variable | Provider | | |
171 | +|----------|----------| | |
172 | +| `OPENAI_API_KEY` | OpenAI (GPT-4, GPT-4o) | | |
173 | +| `ANTHROPIC_API_KEY` | Anthropic (Claude) | | |
174 | +| `GROQ_API_KEY` | Groq (fast inference) | | |
175 | +| `BYTEZ_API_KEY` | Bytez | | |
176 | +| `OPENROUTER_API_KEY` | OpenRouter (multi-model) | | |
177 | +| `TOGETHER_API_KEY` | Together AI | | |
178 | +| `GOOGLE_AI_API_KEY` | Google AI (Gemini) | | |
179 | + | |
180 | +Ollama is supported for local/self-hosted AI. No API key required — just set the Ollama endpoint in the admin dashboard. | |
181 | + | |
182 | +## External Review Agent | |
183 | + | |
184 | +| Variable | Description | | |
185 | +|----------|-------------| | |
186 | +| `EXTERNAL_AGENT_WEBHOOK_URL` | Webhook URL for external AI agent | | |
187 | +| `EXTERNAL_AGENT_API_KEY` | API key for the external agent | | |
188 | +| `EXTERNAL_AGENT_CALLBACK_SECRET` | Callback verification secret | | |
189 | + | |
190 | +## Backup | |
191 | + | |
192 | +| Variable | Description | Default | | |
193 | +|----------|-------------|---------| | |
194 | +| `BACKUP_ENCRYPTION_KEY` | Encryption key for backups | — | | |
195 | +| `BACKUP_S3_BUCKET` | S3 bucket for backups | — | | |
196 | +| `BACKUP_S3_PREFIX` | S3 prefix | `opencodehub-backups` | | |
197 | +| `BACKUP_RETENTION_DAYS` | Retention period | `30` | | |
198 | +| `BACKUP_DIR` | Local backup directory | `./backups` | | |
199 | + | |
200 | +## Air-gapped Mode | |
201 | + | |
202 | +| Variable | Description | Default | | |
203 | +|----------|-------------|---------| | |
204 | +| `AIR_GAPPED_MODE` | Disable all external API calls | `false` | | |
205 | + | |
206 | +When enabled, OpenCodeHub operates fully offline. AI features are disabled unless you run a local model (e.g., Ollama). | |
207 | + | |
208 | +## Observability | |
209 | + | |
59 | 210 | | Variable | Description | |
60 | 211 | |----------|-------------| |
61 | -| `SMTP_HOST` | Hostname (e.g. `smtp.gmail.com`) | | |
62 | -| `SMTP_PORT` | Port (e.g. `587`) | | |
63 | -| `SMTP_USER` | Username | | |
64 | -| `SMTP_PASSWORD`| Password | | |
65 | -| `SMTP_FROM` | From address | | |
212 | +| `GRAFANA_OTLP_ENDPOINT` | Grafana Cloud OTLP endpoint | | |
213 | +| `GRAFANA_INSTANCE_ID` | Grafana Cloud instance ID | | |
214 | +| `GRAFANA_API_KEY` | Grafana Cloud API key | | |
215 | + | |
216 | +OpenCodeHub exports OpenTelemetry traces and Prometheus metrics. See [Monitoring](/administration/monitoring/) for setup details. | |
66 | 217 | |
▼docs-site/src/content/docs/administration/deploy-nas.md
+232-35
| @@ -3,23 +3,42 @@ title: "Deploy on NAS (Synology, TrueNAS, QNAP)" | ||
3 | 3 | description: "Deploy OpenCodeHub on your Network Attached Storage device using Docker" |
4 | 4 | --- |
5 | 5 | |
6 | +# Deploy OpenCodeHub on NAS (Synology, TrueNAS, QNAP) | |
7 | + | |
6 | 8 | > **Difficulty:** Intermediate | **Time:** 30-45 minutes | **Method:** Docker/Container Station |
7 | 9 | |
8 | 10 | 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. |
9 | 11 | |
12 | +> **Recommended:** the repository ships `docker-compose.nas.yml`, which is | |
13 | +> the production-safe version of every snippet below. It runs the web app, | |
14 | +> the **SSH Git server**, and the **background worker** as separate | |
15 | +> containers, requires secrets instead of defaulting them, and binds every | |
16 | +> data directory to one host path (`OCH_DATA_DIR`, default `/mnt/och`) — | |
17 | +> perfect for a dedicated hard disk or NAS share: | |
18 | +> | |
19 | +> ```bash | |
20 | +> cp .env.example .env # then fill in ALL secrets | |
21 | +> OCH_DATA_DIR=/volume1/opencodehub docker compose -f docker-compose.nas.yml up -d | |
22 | +> ``` | |
23 | +> | |
24 | +> The inline YAML in this guide is a minimal reference. Note that the **web | |
25 | +> app container does not run SSH Git** — if you use a snippet as-is, port | |
26 | +> 2222 will not serve SSH. Add the `ssh-git` service (or use | |
27 | +> `docker-compose.nas.yml`) for SSH access. | |
28 | + | |
10 | 29 | --- |
11 | 30 | |
12 | 31 | ## Prerequisites |
13 | 32 | |
14 | 33 | | NAS Type | Minimum Requirements | |
15 | 34 | |----------|---------------------| |
16 | -| **Synology** | DSM 7.0+, 4GB RAM (8GB recommended), x86_64 or ARM64 | | |
35 | +| **Synology** | DSM 7.0+, 4GB RAM (8GB recommended), x86_64 or ARM64 (better performance on x86_64) | | |
17 | 36 | | **TrueNAS SCALE** | 8GB RAM, 2 CPU cores, 40GB free space | |
18 | 37 | | **QNAP** | QTS 5.0+, 4GB RAM, Container Station installed | |
19 | 38 | | **Generic** | Docker/Podman support, 4GB RAM, 2 CPU cores | |
20 | 39 | |
21 | -- A domain with DDNS configured (e.g., `git.yourddns.net`) OR local network access only | |
22 | -- Port forwarding on your router (if accessing externally): | |
40 | +- A domain with DDNS (Dynamic DNS) configured (e.g., `git.yourddns.net`) OR local network access only | |
41 | +- Port forwarding configured on your router (if accessing externally): | |
23 | 42 | - `80` → NAS:80 |
24 | 43 | - `443` → NAS:443 |
25 | 44 | - `2222` → NAS:2222 (for SSH Git, optional) |
| @@ -138,7 +157,6 @@ docker run -d \ | ||
138 | 157 | --link opencodehub-postgres:postgres \ |
139 | 158 | --link opencodehub-redis:redis \ |
140 | 159 | -p 4321:4321 \ |
141 | - -p 2222:2222 \ | |
142 | 160 | -e NODE_ENV=production \ |
143 | 161 | -e DATABASE_URL=postgresql://opencodehub:[email protected]:5432/opencodehub \ |
144 | 162 | -e REDIS_URL=redis://:[email protected]:6379 \ |
| @@ -148,19 +166,24 @@ docker run -d \ | ||
148 | 166 | -e CRON_SECRET=$(openssl rand -hex 32) \ |
149 | 167 | -e RUNNER_SECRET=$(openssl rand -hex 32) \ |
150 | 168 | -e WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) \ |
169 | + -e AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) \ | |
151 | 170 | -e SITE_URL=https://git.yourddns.net \ |
152 | 171 | -e STORAGE_TYPE=local \ |
153 | 172 | -e STORAGE_PATH=/data/storage \ |
154 | - -e REPOS_PATH=/data/repos \ | |
155 | - -e SSH_HOST_KEY_PATH=/data/ssh/host_key \ | |
173 | + -e GIT_REPOS_PATH=/data/repos \ | |
174 | + -e GIT_SSH_HOST_KEY=/data/ssh/host_key \ | |
156 | 175 | -v /volume1/opencodehub/repos:/data/repos \ |
157 | 176 | -v /volume1/opencodehub/storage:/data/storage \ |
158 | 177 | -v /volume1/opencodehub/ssh:/data/ssh \ |
159 | 178 | -v /volume1/opencodehub/cache:/data/cache \ |
160 | - swadhinbiswas/opencodehub:latest | |
179 | + opencodehub/opencodehub:latest | |
161 | 180 | ``` |
162 | 181 | |
163 | 182 | > **Note:** On Synology, `host.docker.internal` may not work. Use your NAS IP address instead (e.g., `192.168.1.100`). |
183 | +> | |
184 | +> **SSH Git:** the app container serves the web UI only. For SSH access, | |
185 | +> run a second container from the same image with `PROCESS_TYPE=ssh` and | |
186 | +> expose port 2222 (this is exactly what `docker-compose.nas.yml` does). | |
164 | 187 | |
165 | 188 | #### Option B: Build from Source |
166 | 189 | |
| @@ -178,9 +201,9 @@ services: | ||
178 | 201 | restart: unless-stopped |
179 | 202 | ports: |
180 | 203 | - "4321:4321" |
181 | - - "2222:2222" | |
182 | 204 | environment: |
183 | 205 | - NODE_ENV=production |
206 | + - PROCESS_TYPE=app | |
184 | 207 | - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub |
185 | 208 | - REDIS_URL=redis://:YOUR_PASS@redis:6379 |
186 | 209 | - JWT_SECRET=YOUR_SECRET |
| @@ -189,11 +212,72 @@ services: | ||
189 | 212 | - CRON_SECRET=YOUR_SECRET |
190 | 213 | - RUNNER_SECRET=YOUR_SECRET |
191 | 214 | - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET |
215 | + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET | |
192 | 216 | - SITE_URL=https://git.yourddns.net |
193 | 217 | - STORAGE_TYPE=local |
194 | 218 | - STORAGE_PATH=/data/storage |
219 | + - GIT_REPOS_PATH=/data/repos | |
220 | + - REPOS_PATH=/data/repos | |
221 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
222 | + - GIT_SSH_PORT=2222 | |
223 | + volumes: | |
224 | + - /volume1/opencodehub/repos:/data/repos | |
225 | + - /volume1/opencodehub/storage:/data/storage | |
226 | + - /volume1/opencodehub/ssh:/data/ssh | |
227 | + - /volume1/opencodehub/cache:/data/cache | |
228 | + depends_on: | |
229 | + - postgres | |
230 | + - redis | |
231 | + networks: | |
232 | + - och-net | |
233 | + | |
234 | + ssh-git: | |
235 | + build: | |
236 | + context: /volume1/docker/OpenCodeHub | |
237 | + dockerfile: Dockerfile | |
238 | + container_name: opencodehub-ssh | |
239 | + restart: unless-stopped | |
240 | + ports: | |
241 | + - "2222:2222" | |
242 | + environment: | |
243 | + - NODE_ENV=production | |
244 | + - PROCESS_TYPE=ssh | |
245 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
246 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
247 | + - JWT_SECRET=YOUR_SECRET | |
248 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
249 | + - GIT_REPOS_PATH=/data/repos | |
250 | + - REPOS_PATH=/data/repos | |
251 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
252 | + - GIT_SSH_PORT=2222 | |
253 | + volumes: | |
254 | + - /volume1/opencodehub/repos:/data/repos | |
255 | + - /volume1/opencodehub/storage:/data/storage | |
256 | + - /volume1/opencodehub/ssh:/data/ssh | |
257 | + - /volume1/opencodehub/cache:/data/cache | |
258 | + depends_on: | |
259 | + - postgres | |
260 | + - redis | |
261 | + networks: | |
262 | + - och-net | |
263 | + | |
264 | + worker: | |
265 | + build: | |
266 | + context: /volume1/docker/OpenCodeHub | |
267 | + dockerfile: Dockerfile | |
268 | + container_name: opencodehub-worker | |
269 | + restart: unless-stopped | |
270 | + environment: | |
271 | + - NODE_ENV=production | |
272 | + - PROCESS_TYPE=worker | |
273 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
274 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
275 | + - JWT_SECRET=YOUR_SECRET | |
276 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
277 | + - GIT_REPOS_PATH=/data/repos | |
195 | 278 | - REPOS_PATH=/data/repos |
196 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
279 | + - STORAGE_TYPE=local | |
280 | + - STORAGE_PATH=/data/storage | |
197 | 281 | volumes: |
198 | 282 | - /volume1/opencodehub/repos:/data/repos |
199 | 283 | - /volume1/opencodehub/storage:/data/storage |
| @@ -304,14 +388,14 @@ version: '3.8' | ||
304 | 388 | |
305 | 389 | services: |
306 | 390 | app: |
307 | - image: swadhinbiswas/opencodehub:latest | |
391 | + image: opencodehub/opencodehub:latest | |
308 | 392 | container_name: opencodehub |
309 | 393 | restart: unless-stopped |
310 | 394 | ports: |
311 | 395 | - "4321:4321" |
312 | - - "2222:2222" | |
313 | 396 | environment: |
314 | 397 | - NODE_ENV=production |
398 | + - PROCESS_TYPE=app | |
315 | 399 | - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub |
316 | 400 | - REDIS_URL=redis://:YOUR_PASS@redis:6379 |
317 | 401 | - JWT_SECRET=YOUR_SECRET |
| @@ -320,11 +404,68 @@ services: | ||
320 | 404 | - CRON_SECRET=YOUR_SECRET |
321 | 405 | - RUNNER_SECRET=YOUR_SECRET |
322 | 406 | - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET |
407 | + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET | |
323 | 408 | - SITE_URL=https://git.yourdomain.com |
324 | 409 | - STORAGE_TYPE=local |
325 | 410 | - STORAGE_PATH=/data/storage |
411 | + - GIT_REPOS_PATH=/data/repos | |
412 | + - REPOS_PATH=/data/repos | |
413 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
414 | + - GIT_SSH_PORT=2222 | |
415 | + volumes: | |
416 | + - /mnt/YOURPOOL/opencodehub/repos:/data/repos | |
417 | + - /mnt/YOURPOOL/opencodehub/storage:/data/storage | |
418 | + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh | |
419 | + - /mnt/YOURPOOL/opencodehub/cache:/data/cache | |
420 | + depends_on: | |
421 | + - postgres | |
422 | + - redis | |
423 | + networks: | |
424 | + - och-net | |
425 | + | |
426 | + ssh-git: | |
427 | + image: opencodehub/opencodehub:latest | |
428 | + container_name: opencodehub-ssh | |
429 | + restart: unless-stopped | |
430 | + ports: | |
431 | + - "2222:2222" | |
432 | + environment: | |
433 | + - NODE_ENV=production | |
434 | + - PROCESS_TYPE=ssh | |
435 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
436 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
437 | + - JWT_SECRET=YOUR_SECRET | |
438 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
439 | + - GIT_REPOS_PATH=/data/repos | |
440 | + - REPOS_PATH=/data/repos | |
441 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
442 | + - GIT_SSH_PORT=2222 | |
443 | + volumes: | |
444 | + - /mnt/YOURPOOL/opencodehub/repos:/data/repos | |
445 | + - /mnt/YOURPOOL/opencodehub/storage:/data/storage | |
446 | + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh | |
447 | + - /mnt/YOURPOOL/opencodehub/cache:/data/cache | |
448 | + depends_on: | |
449 | + - postgres | |
450 | + - redis | |
451 | + networks: | |
452 | + - och-net | |
453 | + | |
454 | + worker: | |
455 | + image: opencodehub/opencodehub:latest | |
456 | + container_name: opencodehub-worker | |
457 | + restart: unless-stopped | |
458 | + environment: | |
459 | + - NODE_ENV=production | |
460 | + - PROCESS_TYPE=worker | |
461 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
462 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
463 | + - JWT_SECRET=YOUR_SECRET | |
464 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
465 | + - GIT_REPOS_PATH=/data/repos | |
326 | 466 | - REPOS_PATH=/data/repos |
327 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
467 | + - STORAGE_TYPE=local | |
468 | + - STORAGE_PATH=/data/storage | |
328 | 469 | volumes: |
329 | 470 | - /mnt/YOURPOOL/opencodehub/repos:/data/repos |
330 | 471 | - /mnt/YOURPOOL/opencodehub/storage:/data/storage |
| @@ -446,7 +587,7 @@ Install Nginx Proxy Manager from the TrueNAS Apps catalog: | ||
446 | 587 | |
447 | 588 | #### Create OpenCodeHub: |
448 | 589 | |
449 | -1. Search and install `swadhinbiswas/opencodehub:latest` | |
590 | +1. Search and install `opencodehub/opencodehub:latest` | |
450 | 591 | 2. Name: `opencodehub` |
451 | 592 | 3. Network: `opencodehub-network` |
452 | 593 | 4. Environment variables (same as Synology example) |
| @@ -485,20 +626,13 @@ Install Nginx Proxy Manager from the TrueNAS Apps catalog: | ||
485 | 626 | |
486 | 627 | ## Part 4: Generic NAS / Any Docker Host |
487 | 628 | |
488 | -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: | |
629 | +If your NAS supports Docker (via Portainer, Podman, or CLI): | |
489 | 630 | |
490 | -```bash | |
491 | -# Connect to your NAS via SSH, then run: | |
492 | -curl -sSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash | |
493 | -``` | |
494 | - | |
495 | -### Using Docker Compose (Manual Setup) | |
496 | - | |
497 | -If you prefer to deploy manually via Portainer or `docker-compose.yml`: | |
631 | +### Using Docker Compose: | |
498 | 632 | |
499 | 633 | ```bash |
500 | -# Create unified data directory | |
501 | -mkdir -p ~/opencodehub-data | |
634 | +# Create directories | |
635 | +mkdir -p ~/opencodehub/{repos,storage,ssh,cache,postgres,redis} | |
502 | 636 | |
503 | 637 | # Create docker-compose.yml |
504 | 638 | cat > ~/opencodehub/docker-compose.yml << 'EOF' |
| @@ -506,14 +640,14 @@ version: '3.8' | ||
506 | 640 | |
507 | 641 | services: |
508 | 642 | app: |
509 | - image: swadhinbiswas/opencodehub:latest | |
643 | + image: opencodehub/opencodehub:latest | |
510 | 644 | container_name: opencodehub |
511 | 645 | restart: unless-stopped |
512 | 646 | ports: |
513 | 647 | - "4321:4321" |
514 | - - "2222:2222" | |
515 | 648 | environment: |
516 | 649 | - NODE_ENV=production |
650 | + - PROCESS_TYPE=app | |
517 | 651 | - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub |
518 | 652 | - REDIS_URL=redis://:YOUR_PASS@redis:6379 |
519 | 653 | - JWT_SECRET=YOUR_SECRET |
| @@ -522,10 +656,73 @@ services: | ||
522 | 656 | - CRON_SECRET=YOUR_SECRET |
523 | 657 | - RUNNER_SECRET=YOUR_SECRET |
524 | 658 | - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET |
659 | + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET | |
525 | 660 | - SITE_URL=https://git.yourdomain.com |
526 | - - DATA_DIR=/data | |
661 | + - STORAGE_TYPE=local | |
662 | + - STORAGE_PATH=/data/storage | |
663 | + - GIT_REPOS_PATH=/data/repos | |
664 | + - REPOS_PATH=/data/repos | |
665 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
666 | + - GIT_SSH_PORT=2222 | |
667 | + volumes: | |
668 | + - ./repos:/data/repos | |
669 | + - ./storage:/data/storage | |
670 | + - ./ssh:/data/ssh | |
671 | + - ./cache:/data/cache | |
672 | + depends_on: | |
673 | + - postgres | |
674 | + - redis | |
675 | + networks: | |
676 | + - och-net | |
677 | + | |
678 | + ssh-git: | |
679 | + image: opencodehub/opencodehub:latest | |
680 | + container_name: opencodehub-ssh | |
681 | + restart: unless-stopped | |
682 | + ports: | |
683 | + - "2222:2222" | |
684 | + environment: | |
685 | + - NODE_ENV=production | |
686 | + - PROCESS_TYPE=ssh | |
687 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
688 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
689 | + - JWT_SECRET=YOUR_SECRET | |
690 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
691 | + - GIT_REPOS_PATH=/data/repos | |
692 | + - REPOS_PATH=/data/repos | |
693 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
694 | + - GIT_SSH_PORT=2222 | |
695 | + volumes: | |
696 | + - ./repos:/data/repos | |
697 | + - ./storage:/data/storage | |
698 | + - ./ssh:/data/ssh | |
699 | + - ./cache:/data/cache | |
700 | + depends_on: | |
701 | + - postgres | |
702 | + - redis | |
703 | + networks: | |
704 | + - och-net | |
705 | + | |
706 | + worker: | |
707 | + image: opencodehub/opencodehub:latest | |
708 | + container_name: opencodehub-worker | |
709 | + restart: unless-stopped | |
710 | + environment: | |
711 | + - NODE_ENV=production | |
712 | + - PROCESS_TYPE=worker | |
713 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
714 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
715 | + - JWT_SECRET=YOUR_SECRET | |
716 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
717 | + - GIT_REPOS_PATH=/data/repos | |
718 | + - REPOS_PATH=/data/repos | |
719 | + - STORAGE_TYPE=local | |
720 | + - STORAGE_PATH=/data/storage | |
527 | 721 | volumes: |
528 | - - ~/opencodehub-data:/data | |
722 | + - ./repos:/data/repos | |
723 | + - ./storage:/data/storage | |
724 | + - ./ssh:/data/ssh | |
725 | + - ./cache:/data/cache | |
529 | 726 | depends_on: |
530 | 727 | - postgres |
531 | 728 | - redis |
| @@ -541,7 +738,7 @@ services: | ||
541 | 738 | - POSTGRES_PASSWORD=YOUR_PASS |
542 | 739 | - POSTGRES_DB=opencodehub |
543 | 740 | volumes: |
544 | - - ~/opencodehub-data/postgres:/var/lib/postgresql/data | |
741 | + - ./postgres:/var/lib/postgresql/data | |
545 | 742 | networks: |
546 | 743 | - och-net |
547 | 744 | |
| @@ -551,7 +748,7 @@ services: | ||
551 | 748 | restart: unless-stopped |
552 | 749 | command: redis-server --appendonly yes --requirepass YOUR_PASS |
553 | 750 | volumes: |
554 | - - ~/opencodehub-data/redis:/data | |
751 | + - ./redis:/data | |
555 | 752 | networks: |
556 | 753 | - och-net |
557 | 754 | |
| @@ -715,14 +912,14 @@ OpenCodeHub on NAS typically uses: | ||
715 | 912 | |
716 | 913 | ### QNAP |
717 | 914 | - Use **QuTS hero** (ZFS) for better data integrity |
718 | -- Enable **snapshots** for the opencodehub dataset | |
915 | +- Enable ** snapshots** for the opencodehub dataset | |
719 | 916 | - **QVPN** can secure remote access without exposing ports |
720 | 917 | |
721 | 918 | --- |
722 | 919 | |
723 | 920 | ## Next Steps |
724 | 921 | |
725 | -- [Configure Email (SMTP)](/administration/configuration/#email) | |
726 | -- [Set up Offsite Storage (S3/R2)](/guides/storage-adapters/) — highly recommended for NAS | |
727 | -- [Enable Branch Protection](/guides/branch-protection/) | |
728 | -- [Set up your First Stack](/tutorials/your-first-stack/) | |
922 | +- [Configure Email (SMTP)](../administration/configuration.md#email) | |
923 | +- [Set up Offsite Storage (S3/R2)](../guides/storage-adapters.md) — highly recommended for NAS (protects against drive failure) | |
924 | +- [Enable Branch Protection](../guides/branch-protection.md) | |
925 | +- [Set up your First Stack](../tutorials/your-first-stack.md) | |
729 | 926 | |
▼docs-site/src/content/docs/administration/deployment.md
+38-41
| @@ -105,19 +105,9 @@ STORAGE_SECRET_ACCESS_KEY=<your-secret-key> | ||
105 | 105 | > removed; use the `s3` driver with a vendor-specific `STORAGE_ENDPOINT` |
106 | 106 | > for object storage. See `docs/guides/storage-adapters.md` for the |
107 | 107 | > full list of compatible object stores. |
108 | -```bash | |
109 | -STORAGE_TYPE=gdrive | |
110 | -GOOGLE_CLIENT_ID=... | |
111 | -GOOGLE_CLIENT_SECRET=... | |
112 | -GOOGLE_REFRESH_TOKEN=... | |
113 | -GOOGLE_FOLDER_ID=... | |
114 | -``` | |
115 | 108 | |
116 | 109 | **Optional but Recommended:** |
117 | 110 | ```bash |
118 | -# Error Monitoring | |
119 | -SENTRY_DSN=https://your-sentry-dsn | |
120 | - | |
121 | 111 | # Email |
122 | 112 | SMTP_HOST=smtp.example.com |
123 | 113 | SMTP_PORT=587 |
| @@ -126,7 +116,7 @@ SMTP_PASSWORD=<smtp-password> | ||
126 | 116 | |
127 | 117 | |
128 | 118 | # Redis (for session management) |
129 | -REDIS_URL=redis://localhost:6379 | |
119 | +REDIS_URL=redis://:your-password@localhost:6379 | |
130 | 120 | ``` |
131 | 121 | |
132 | 122 | ### Deployment Steps |
| @@ -147,11 +137,9 @@ nano .env # Configure all production values | ||
147 | 137 | bun run src/lib/env-validation.ts |
148 | 138 | # Should output: ✅ Environment validation passed |
149 | 139 | |
150 | -# 5. Run database migrations | |
151 | -npm run db:push | |
152 | -# or generate versioned migrations: | |
153 | -npm run db:generate | |
154 | -npm run db:migrate | |
140 | +# 5. Run database migrations (applies committed drizzle/ migrations) | |
141 | +bun run migrate | |
142 | +# or for dev: npm run db:push | |
155 | 143 | |
156 | 144 | # 6. Create admin user |
157 | 145 | bun run scripts/seed-admin.ts |
| @@ -160,59 +148,68 @@ bun run scripts/seed-admin.ts | ||
160 | 148 | # 7. Build for production |
161 | 149 | npm run build |
162 | 150 | |
163 | -# 8. Start production server | |
164 | -npm start | |
151 | +# 8. Start the production server (built in step 7) | |
152 | +npm start # → node ./dist/server/entry.mjs | |
165 | 153 | ``` |
166 | 154 | |
155 | +> **Docker** is the recommended path for production. Use the repository's | |
156 | +> `docker-compose.yml` (quickstart) or `docker-compose.production.yml` | |
157 | +> (Caddy TLS + isolated networks) — do not hand-roll a compose file. | |
158 | + | |
167 | 159 | ### Docker Deployment (Recommended) |
168 | 160 | |
169 | -```yaml | |
170 | -# docker-compose.prod.yml | |
171 | -version: '3.8' | |
161 | +Use the repository's own compose files — they are kept in sync with the | |
162 | +codebase and are production-safe (no default secrets, migrations applied | |
163 | +at boot, worker + SSH services included): | |
164 | + | |
165 | +```bash | |
166 | +# Quickstart (app + SSH + worker + PostgreSQL + Redis) | |
167 | +cp .env.example .env # then set ALL secrets | |
168 | +docker compose up -d | |
169 | + | |
170 | +# Production profile (adds Caddy TLS termination + isolated networks) | |
171 | +docker compose -f docker-compose.production.yml --profile production up -d | |
172 | + | |
173 | +# NAS / dedicated-disk deployments | |
174 | +OCH_DATA_DIR=/mnt/nas/opencodehub docker compose -f docker-compose.nas.yml up -d | |
175 | +``` | |
172 | 176 | |
177 | +Minimal reference compose (for illustration only): | |
178 | + | |
179 | +```yaml | |
173 | 180 | services: |
174 | 181 | app: |
175 | 182 | build: . |
176 | 183 | restart: always |
177 | 184 | ports: |
178 | - - "3000:3000" | |
179 | - env_file: .env.production | |
185 | + - "4321:4321" | |
186 | + env_file: .env | |
187 | + environment: | |
188 | + NODE_ENV: production | |
180 | 189 | depends_on: |
181 | 190 | - postgres |
182 | 191 | - redis |
183 | - volumes: | |
184 | - - ./data:/app/data | |
185 | - environment: | |
186 | - NODE_ENV: production | |
187 | - healthcheck: | |
188 | - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] | |
189 | - interval: 30s | |
190 | - timeout: 10s | |
191 | - retries: 3 | |
192 | 192 | |
193 | 193 | postgres: |
194 | - image: postgres:15-alpine | |
194 | + image: postgres:16-alpine | |
195 | 195 | restart: always |
196 | 196 | environment: |
197 | 197 | POSTGRES_DB: opencodehub |
198 | 198 | POSTGRES_USER: opencodehub |
199 | 199 | POSTGRES_PASSWORD: ${DATABASE_PASSWORD} |
200 | - volumes: | |
201 | - - postgres_data:/var/lib/postgresql/data | |
202 | - ports: | |
203 | - - "5432:5432" | |
204 | 200 | |
205 | 201 | redis: |
206 | 202 | image: redis:7-alpine |
207 | 203 | restart: always |
208 | 204 | command: redis-server --requirepass ${REDIS_PASSWORD} |
209 | - ports: | |
210 | - - "6379:6379" | |
211 | 205 | |
212 | 206 | volumes: |
213 | 207 | postgres_data: |
214 | 208 | ``` |
215 | 209 | |
210 | +> Exposed ports: **4321** (web/API/Git HTTP) and **2222** (SSH Git). | |
211 | +> PostgreSQL (5432) and Redis (6379) stay on an internal network. | |
212 | + | |
216 | 213 | ```bash |
217 | 214 | # Deploy with Docker |
218 | 215 | docker-compose -f docker-compose.prod.yml up -d |
| @@ -243,7 +240,7 @@ server { | ||
243 | 240 | |
244 | 241 | # Proxy configuration |
245 | 242 | location / { |
246 | - proxy_pass http://localhost:3000; | |
243 | + proxy_pass http://localhost:4321; | |
247 | 244 | proxy_set_header Host $host; |
248 | 245 | proxy_set_header X-Real-IP $remote_addr; |
249 | 246 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| @@ -257,7 +254,7 @@ server { | ||
257 | 254 | |
258 | 255 | # Git operations (larger timeouts) |
259 | 256 | location ~ ^/[^/]+/[^/]+\.git/ { |
260 | - proxy_pass http://localhost:3000; | |
257 | + proxy_pass http://localhost:4321; | |
261 | 258 | proxy_read_timeout 600s; |
262 | 259 | proxy_send_timeout 600s; |
263 | 260 | proxy_buffer_size 128k; |
264 | 261 | |
▼docs-site/src/content/docs/administration/expose-nas.md
+9-9
| @@ -42,7 +42,7 @@ firewall never sees an inbound connection to OpenCodeHub ports. | ||
42 | 42 | |
43 | 43 | Whatever exposure method you pick, you need: |
44 | 44 | |
45 | -1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `3000`). | |
45 | +1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `4321`). | |
46 | 46 | 2. **A reachable hostname** (Tailscale or Cloudflare) pointed at the tunnel. |
47 | 47 | 3. **A persistent `cloudflared` or `tailscaled`** process — running on the NAS, in Docker, or as a system service. |
48 | 48 | 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 | ||
104 | 104 | # in the Tailscale admin console: https://login.tailscale.com/admin |
105 | 105 | # (turn on the "Funnel" feature flag). |
106 | 106 | # |
107 | -# Then on the NAS, expose the port OpenCodeHub listens on (default 3000): | |
108 | -sudo tailscale funnel 3000 on | |
107 | +# Then on the NAS, expose the port OpenCodeHub listens on (default 4321): | |
108 | +sudo tailscale funnel 4321 on | |
109 | 109 | |
110 | 110 | # Or expose a specific path with HTTPS-only: |
111 | -sudo tailscale funnel --https=443 http://localhost:3000 | |
111 | +sudo tailscale funnel --https=443 http://localhost:4321 | |
112 | 112 | ``` |
113 | 113 | |
114 | 114 | 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 | ||
136 | 136 | run: |
137 | 137 | |
138 | 138 | ```bash |
139 | -sudo tailscale funnel 3000 on | |
139 | +sudo tailscale funnel 4321 on | |
140 | 140 | ``` |
141 | 141 | |
142 | 142 | For TrueNAS SCALE, configure the Tailscale app's "Post-Start" command |
| @@ -266,11 +266,11 @@ In the Cloudflare dashboard → your tunnel → **Public Hostname**: | ||
266 | 266 | |---|---| |
267 | 267 | | Subdomain | `git` (or anything) | |
268 | 268 | | Domain | `example.com` | |
269 | -| **Service** | Type: `HTTP`, URL: `opencodehub:3000` (or `localhost:3000` if `cloudflared` is host-networking) | | |
269 | +| **Service** | Type: `HTTP`, URL: `opencodehub:4321` (or `localhost:4321` if `cloudflared` is host-networking) | | |
270 | 270 | |
271 | 271 | If you used Option A (Docker bridge network), `opencodehub` must be the |
272 | 272 | service name in your OpenCodeHub `docker-compose.yml`. If you used |
273 | -host networking, use `localhost:3000`. | |
273 | +host networking, use `localhost:4321`. | |
274 | 274 | |
275 | 275 | Save. After a few seconds, `https://git.example.com` should reach your |
276 | 276 | OpenCodeHub instance. |
| @@ -382,8 +382,8 @@ on the NAS itself. | ||
382 | 382 | - Cloudflare: the tunnel status is **HEALTHY** in the Zero Trust |
383 | 383 | dashboard. |
384 | 384 | - Confirm OpenCodeHub is reachable from the tunnel's network namespace: |
385 | - - Tailscale (host networking): `curl http://localhost:3000` | |
386 | - - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:3000/` | |
385 | + - Tailscale (host networking): `curl http://localhost:4321` | |
386 | + - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:4321/` | |
387 | 387 | |
388 | 388 | ### Real client IP is wrong |
389 | 389 | |
390 | 390 | |
▼docs-site/src/content/docs/administration/horizontal-scaling.md
+17-0
| @@ -0,0 +1,17 @@ | ||
1 | +# Horizontal Scaling | |
2 | + | |
3 | +OpenCodeHub is designed to be scaled horizontally to handle high traffic and large workloads. | |
4 | + | |
5 | +## Stateless Application Servers | |
6 | +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). | |
7 | + | |
8 | +## State Management | |
9 | +- **Database**: Ensure your database (PostgreSQL) is appropriately scaled or managed (e.g., AWS RDS). | |
10 | +- **Redis**: Redis is strictly required for horizontal scaling. It handles sessions, rate limiting, distributed locking, and BullMQ queues for the background workers. | |
11 | +- **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. | |
12 | + | |
13 | +## Shared Git Storage | |
14 | +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. | |
15 | + | |
16 | +## Pluggable Blob Storage | |
17 | +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. | |
0 | 18 | |
▼docs-site/src/content/docs/administration/monitoring.md
+2-0
| @@ -12,6 +12,8 @@ The minimum production observability stack should include: | ||
12 | 12 | |
13 | 13 | - `GET /api/health` for liveness/readiness |
14 | 14 | - `GET /api/metrics` for Prometheus scraping |
15 | + (protected by `METRICS_TOKEN` when set — pass it as a Bearer token in the | |
16 | + scrape config; see `deploy/prometheus.yml`) | |
15 | 17 | - Grafana dashboards for request latency, queue health, and storage/DB timing |
16 | 18 | - Alerting for p95 regressions and service degradation |
17 | 19 | |
18 | 20 | |
▼docs-site/src/content/docs/development/architecture.md
+105-54
| @@ -3,57 +3,108 @@ title: "System Architecture" | ||
3 | 3 | description: "High-level overview of OpenCodeHub's components and data flow." |
4 | 4 | --- |
5 | 5 | |
6 | -import { Tabs, TabItem } from '@astrojs/starlight/components'; | |
7 | - | |
8 | -OpenCodeHub is built for performance and scalability, using a modern stack optimized for self-hosting. | |
9 | - | |
10 | -## C4 Container Diagram | |
11 | - | |
12 | -This diagram visualizes the high-level interaction between the user, the CLI, and the server infrastructure. | |
13 | - | |
14 | -C4Container | |
15 | - title Container diagram for OpenCodeHub System | |
16 | - | |
17 | - Person(developer, "Developer", "Uses CLI and Web UI to manage code") | |
18 | - | |
19 | - System_Boundary(c1, "OpenCodeHub Platform") { | |
20 | - Container(cli, "CLI Tool", "TypeScript, Node.js", "Handles stacking, pushing, and rapid stack manipulation") | |
21 | - Container(web_app, "Web Application", "Astro, SSR", "Serves UI and handles API requests") | |
22 | - Container(git_server, "Git Server", "Node.js Stream", "Smart HTTP Protocol handler for cloning/pushing") | |
23 | - | |
24 | - ContainerDb(database, "PostgreSQL", "SQL Database", "Stores user data, PR metadata, and Merge Queue state") | |
25 | - ContainerDb(redis, "Redis", "In-Memory", "Cache, Session Storage & Realtime Pub/Sub") | |
26 | - ContainerDb(s3, "Object Storage", "S3 / MinIO", "Stores raw Git packfiles and LFS objects") | |
27 | - } | |
28 | - | |
29 | - Rel(developer, cli, "Uses", "Terminal") | |
30 | - Rel(developer, web_app, "Visits", "HTTPS") | |
31 | - | |
32 | - Rel(cli, web_app, "API Calls", "JSON/REST") | |
33 | - Rel(cli, git_server, "Git Push/Pull", "HTTPS/Smart Protocol") | |
34 | - | |
35 | - Rel(web_app, database, "Reads/Writes", "SQL/Drizzle") | |
36 | - Rel(web_app, redis, "Uses", "Cache & Sessions") | |
37 | - | |
38 | - Rel(git_server, s3, "Streams Objects", "S3 API") | |
39 | - Rel(web_app, s3, "Reads Objects", "S3 API") | |
40 | - | |
41 | -## Component Breakdown | |
42 | - | |
43 | -<Tabs> | |
44 | - <TabItem label="Web App (Astro)"> | |
45 | - The core of the system. Built with **Astro** in SSR mode. | |
46 | - - **Frontend**: Lightweight, HTML-first pages with islands architecture. | |
47 | - - **API**: Functional endpoints for the CLI and third-party tools. | |
48 | - - **Auth**: Lucia Auth for secure session management. | |
49 | - </TabItem> | |
50 | - <TabItem label="Git Server"> | |
51 | - A custom implementation of the Git Smart HTTP protocol. | |
52 | - - **Performance**: Streams packfiles directly to/from S3 without buffering to disk. | |
53 | - - **Stateless**: Can be scaled horizontally across multiple nodes. | |
54 | - </TabItem> | |
55 | - <TabItem label="Storage Layer"> | |
56 | - - **Metadata**: PostgreSQL handles the complex relationships between PRs, comments, and repos. | |
57 | - - **Git Data**: We treat Git objects as immutable blobs, perfect for S3-compatible storage (AWS, Cloudflare R2, MinIO). | |
58 | - </TabItem> | |
59 | -</Tabs> | |
6 | +OpenCodeHub is a modular monolith built for performance and self-hosting simplicity. | |
7 | + | |
8 | +## System Overview | |
9 | + | |
10 | +``` | |
11 | +┌────────────────────── ───────────────────────────────────────┐ | |
12 | +│ CLIENTS │ | |
13 | +│ Browser │ Git CLI (HTTP/SSH) │ OpenCodeHub CLI (och) │ | |
14 | +└─────────────────────────────────────────────────────────────┘ | |
15 | + │ | |
16 | +┌─────────────────────────────────────────────────────────────┐ | |
17 | +│ OPENCODEHUB PLATFORM │ | |
18 | +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ | |
19 | +│ │ Web UI │ │ REST API │ │ GraphQL Endpoint │ │ | |
20 | +│ │ (Astro+React)│ │ (175+ routes)│ │ (src/pages/api/) │ │ | |
21 | +│ └─────────────┘ └─────────────┘ └─────────────────────┘ │ | |
22 | +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ | |
23 | +│ │ Git Server │ │ SSH Server │ │ Pipeline Runner │ │ | |
24 | +│ │ (HTTP RPC) │ │ (ssh2) │ │ (Docker executor) │ │ | |
25 | +│ └───── ────────┘ └─────────────┘ └─────────────────────┘ │ | |
26 | +└─────────────────────────────────────────────────────────────┘ | |
27 | + │ | |
28 | +┌─────────────────────────────────────────────────────────────┐ | |
29 | +│ PERSISTENCE & INFRASTRUCTURE │ | |
30 | +│ PostgreSQL/SQLite/Turso │ Redis │ Pluggable Storage │ | |
31 | +└─────────────────────────────────────────────────────────────┘ | |
32 | +``` | |
33 | + | |
34 | +## Runtime Processes | |
35 | + | |
36 | +| Process | Command | Purpose | | |
37 | +|---------|---------|---------| | |
38 | +| Main App | `npm run dev` | Web UI + API + Git HTTP | | |
39 | +| SSH Git | `npm run git:start` | SSH git push/pull server | | |
40 | +| Worker | `npm run worker:start` | Background jobs (BullMQ queues) | | |
41 | +| Runner | `npm run runner:start` | CI/CD pipeline execution (Docker) | | |
42 | + | |
43 | +## Tech Stack | |
44 | + | |
45 | +| Layer | Technology | | |
46 | +|-------|-----------| | |
47 | +| Framework | Astro 4.x (SSR mode, `@astrojs/node` standalone adapter) | | |
48 | +| UI | React 18 + Tailwind CSS + Radix UI primitives | | |
49 | +| Database ORM | Drizzle ORM | | |
50 | +| DB Drivers | PostgreSQL (pg), SQLite (better-sqlite3), LibSQL/Turso | | |
51 | +| Auth | JWT (jose) + bcryptjs + TOTP (otplib) + OAuth (arctic) + SSO/SAML/OIDC | | |
52 | +| Git | Native `git` CLI + `simple-git` + `isomorphic-git` + `nodegit` | | |
53 | +| SSH | `ssh2` library | | |
54 | +| CI/CD | Dockerode (Docker API) | | |
55 | +| Storage | Abstract adapter: `local` filesystem or `s3`-compatible | | |
56 | +| AI | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter, Together, Bytez | | |
57 | +| Queue | BullMQ + Redis | | |
58 | +| Observability | OpenTelemetry + Prometheus + Pino | | |
59 | + | |
60 | +## Request Flow | |
61 | + | |
62 | +``` | |
63 | +Browser/Git CLI → Astro middleware (auth, rate limit, CSRF) | |
64 | + ↓ | |
65 | + Route handler (src/pages/api/*.ts) | |
66 | + ↓ | |
67 | + Business logic (src/lib/*.ts) | |
68 | + ↓ | |
69 | + Database (src/db/index.ts → Drizzle) | |
70 | +``` | |
71 | + | |
72 | +## Authentication | |
73 | + | |
74 | +- **Web**: Cookie-based JWT (`och_session`) | |
75 | +- **API**: Bearer token (JWT or Personal Access Token `och_...`) | |
76 | +- **Git HTTP**: Basic auth with PAT as password | |
77 | +- **Git SSH**: Public key auth against `deployKeys` table | |
78 | +- **SSO**: SAML and OIDC via admin-configured providers | |
79 | + | |
80 | +## Git Protocol Handling | |
81 | + | |
82 | +- **HTTP**: `src/pages/git/` routes → `src/lib/git-server.ts` → spawns `git upload-pack/receive-pack` | |
83 | +- **SSH**: `src/lib/ssh.ts` using `ssh2` library → spawns git processes | |
84 | +- **Smart HTTP**: Full pkt-line protocol implementation with sideband | |
85 | + | |
86 | +## Storage Abstraction | |
87 | + | |
88 | +All blob storage goes through `StorageAdapter`: | |
89 | +- `LocalStorageAdapter` — filesystem at `STORAGE_PATH` | |
90 | +- `S3StorageAdapter` — any S3-compatible store (AWS, MinIO, R2, Garage, Wasabi, etc.) | |
91 | + | |
92 | +Path-style addressing is enabled automatically when `STORAGE_ENDPOINT` is set. | |
93 | + | |
94 | +## Database | |
95 | + | |
96 | +`src/db/index.ts` provides a factory pattern: | |
97 | +- Reads `DATABASE_DRIVER` and `DATABASE_URL` | |
98 | +- Returns appropriate Drizzle instance | |
99 | +- **Production**: PostgreSQL required (schemas use `pgTable`) | |
100 | +- **Dev**: SQLite or Turso for simplicity | |
101 | + | |
102 | +38 schema tables cover users, repos, PRs, issues, projects, workflows, merge queue, stacked PRs, AI reviews, security policies, and more. | |
103 | + | |
104 | +## Background Workers | |
105 | + | |
106 | +The worker process (`npm run worker:start`) handles: | |
107 | +- BullMQ job queues for webhooks, notifications, CI triggers | |
108 | +- Cron jobs (garbage collection, mirror sync, notification digests) | |
109 | +- Email delivery via SMTP | |
110 | +- Real-time SSE event broadcasting | |
60 | 111 | |
▼docs-site/src/content/docs/development/contributing.md
+78-41
| @@ -2,102 +2,136 @@ | ||
2 | 2 | title: "Contributing Guide" |
3 | 3 | --- |
4 | 4 | |
5 | - | |
6 | 5 | Thank you for your interest in contributing to OpenCodeHub! We welcome contributions from everyone. |
7 | 6 | |
8 | -## 🛠 Local Development Setup | |
9 | - | |
10 | -To contribute code, you need to set up the project locally. | |
7 | +## Local Development Setup | |
11 | 8 | |
12 | 9 | ### Prerequisites |
13 | 10 | |
14 | -- **Node.js**: v18 or v20 (LTS). | |
15 | -- **Bun**: v1.0+ (Our package manager/runner). | |
16 | -- **Docker**: For running the database locally. | |
11 | +- **Node.js** 20+ (or Bun 1.0+) | |
12 | +- **Docker** (for PostgreSQL and Redis) | |
13 | +- **Git** 2.30+ | |
17 | 14 | |
18 | 15 | ### Step 1: Clone & Install |
19 | 16 | |
20 | 17 | ```bash |
21 | 18 | git clone https://github.com/swadhinbiswas/OpencodeHub.git |
22 | 19 | cd OpencodeHub |
23 | - | |
24 | -# Install dependencies (fast!) | |
25 | -bun install | |
20 | +npm install | |
26 | 21 | ``` |
27 | 22 | |
28 | 23 | ### Step 2: Database Setup |
29 | 24 | |
30 | -Start a local Postgres and Redis instance using Docker: | |
25 | +Start PostgreSQL and Redis: | |
31 | 26 | |
32 | 27 | ```bash |
33 | 28 | docker-compose up -d postgres redis |
34 | 29 | ``` |
35 | 30 | |
36 | -Copy the environment config: | |
31 | +Copy and configure environment: | |
32 | + | |
37 | 33 | ```bash |
38 | 34 | cp .env.example .env |
35 | +# The default .env.example works with docker-compose services | |
39 | 36 | ``` |
40 | -*Note: The default `.env.example` is pre-configured to work with the docker-compose services.* | |
41 | 37 | |
42 | -Push the schema to the database: | |
38 | +Push schema to database: | |
39 | + | |
43 | 40 | ```bash |
44 | -bun db:push | |
41 | +npm run db:push | |
45 | 42 | ``` |
46 | 43 | |
47 | -### Step 3: Run the App | |
48 | - | |
49 | -Start the development server: | |
44 | +### Step 3: Seed & Run | |
50 | 45 | |
51 | 46 | ```bash |
52 | -bun dev | |
47 | +# Create admin user | |
48 | +bun run scripts/seed-admin.ts | |
49 | + | |
50 | +# Start dev server | |
51 | +npm run dev | |
53 | 52 | ``` |
54 | 53 | |
55 | -Visit `http://localhost:3000`. | |
54 | +Visit `http://localhost:4321`. | |
56 | 55 | |
56 | +## Running Tests | |
57 | 57 | |
58 | -## 🧪 Running Tests | |
59 | - | |
60 | -We use **Vitest** for unit tests. Use TDD! | |
58 | +We use **Vitest** for unit/integration tests and **Playwright** for E2E tests. | |
61 | 59 | |
62 | 60 | ```bash |
63 | 61 | # Run all tests |
64 | -bun test | |
62 | +npm run test | |
63 | + | |
64 | +# Run with coverage | |
65 | +npm run test:coverage | |
65 | 66 | |
66 | 67 | # Run specific test file |
67 | -bun test user.test.ts | |
68 | +npx vitest run tests/unit/auth.test.ts | |
68 | 69 | |
69 | 70 | # Watch mode |
70 | -bun test --watch | |
71 | +npx vitest --watch | |
71 | 72 | ``` |
72 | 73 | |
73 | - | |
74 | -## 🎨 Code Style | |
74 | +**Current status: 546 tests passing across 114 test files (100% pass rate)** | |
75 | 75 | |
76 | -We use **ESLint** and **Prettier** to enforce code style. | |
76 | +## Code Quality | |
77 | 77 | |
78 | -- **Lint**: `bun lint` | |
79 | -- **Format**: `bun format` | |
78 | +```bash | |
79 | +npm run lint # Astro check | |
80 | +npm run typecheck # TypeScript check | |
81 | +npm run test # Test suite | |
82 | +``` | |
80 | 83 | |
81 | 84 | ### Commit Messages |
82 | 85 | |
83 | -We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification. | |
86 | +We follow [Conventional Commits](https://www.conventionalcommits.org/): | |
84 | 87 | |
85 | 88 | - `feat: add new login page` |
86 | 89 | - `fix: resolve crash on startup` |
87 | 90 | - `docs: update troubleshooting guide` |
91 | +- `refactor: simplify permission check` | |
92 | +- `test: add coverage for merge queue` | |
88 | 93 | |
94 | +## Submitting a Pull Request | |
95 | + | |
96 | +1. **Fork** the repository | |
97 | +2. Create a **feature branch**: `git checkout -b feat/my-feature` | |
98 | +3. Make your changes with tests | |
99 | +4. Run `npm run test` and `npm run typecheck` to verify | |
100 | +5. Commit with a conventional commit message | |
101 | +6. Push to your fork | |
102 | +7. Open a **Pull Request** against `main` | |
103 | +8. Wait for CI checks to pass | |
104 | + | |
105 | +## Project Structure | |
106 | + | |
107 | +``` | |
108 | +src/ | |
109 | +├── pages/ # Astro file-based routing | |
110 | +│ ├── api/ # REST API routes (175+ endpoints) | |
111 | +│ └── [owner]/[repo]/ # Repository pages | |
112 | +├── lib/ # Core business logic (130+ modules) | |
113 | +├── db/ # Database layer (Drizzle ORM) | |
114 | +│ ├── schema/ # 38 schema table definitions | |
115 | +│ └── index.ts # DB connection factory | |
116 | +├── components/ # React components | |
117 | +├── runner/ # CI runner (Docker executor) | |
118 | +└── middleware.ts # Auth, rate limit, CSRF | |
119 | + | |
120 | +cli/ | |
121 | +├── src/commands/ # CLI command groups (20+) | |
122 | +└── src/lib/ # CLI utilities | |
123 | + | |
124 | +docs-site/ # This documentation site (Starlight) | |
125 | +``` | |
89 | 126 | |
90 | -## 🚀 Submitting a Pull Request | |
127 | +## Areas for Contribution | |
91 | 128 | |
92 | -1. **Fork** the repository. | |
93 | -2. Create a **feature branch**: `git checkout -b feat/my-feature`. | |
94 | -3. Commit your changes. | |
95 | -4. Push to your fork. | |
96 | -5. Open a **Pull Request**. | |
97 | -6. Wait for CI checks to pass. | |
98 | -7. A team member will review your code! | |
129 | +- **CI/CD Features**: Matrix builds, caching, workflow templates | |
130 | +- **Package Registry**: npm and Docker registry enhancements | |
131 | +- **AI Review**: New provider integrations, review quality improvements | |
132 | +- **CLI**: New commands, better error messages | |
133 | +- **Tests**: Increase coverage, especially for edge cases | |
134 | +- **Documentation**: Tutorials, guides, API examples | |
135 | +- **Accessibility**: WCAG compliance, keyboard navigation | |
99 | 136 | |
100 | -Thank you for helping build OpenCodeHub! ❤️ | |
137 | +Thank you for helping build OpenCodeHub! | |
101 | 138 | |
▼docs-site/src/content/docs/development/database-schema.md
+65-7
| @@ -2,15 +2,60 @@ | ||
2 | 2 | title: "Database Schema" |
3 | 3 | --- |
4 | 4 | |
5 | +OpenCodeHub uses Drizzle ORM with 38 schema tables defined in `src/db/schema/`. | |
5 | 6 | |
6 | -The database schema is defined using Drizzle ORM in `drizzle/schema.ts`. | |
7 | +## Schema Files | |
7 | 8 | |
8 | -## Core Tables | |
9 | +| File | Tables | Purpose | | |
10 | +|------|--------|---------| | |
11 | +| `users.ts` | `users` | User accounts, passwords, OAuth, 2FA, admin flags | | |
12 | +| `repositories.ts` | `repositories` | Git repos, visibility, disk paths, settings | | |
13 | +| `pull-requests.ts` | `pullRequests` | PRs, states, branches, merge status | | |
14 | +| `issues.ts` | `issues` | Issue tracking | | |
15 | +| `projects.ts` | `projects` | Project boards, columns, cards | | |
16 | +| `stacked-prs.ts` | `prStacks`, `prStackEntries` | Stacked PR relationships | | |
17 | +| `merge-queue.ts` | `mergeQueue` | Queue entries with priority, CI status, attempts | | |
18 | +| `workflows.ts` | `workflows`, `workflowRuns`, `workflowJobs`, `workflowSteps` | CI/CD pipeline execution | | |
19 | +| `pipeline-runners.ts` | `pipelineRunners` | Registered CI runners | | |
20 | +| `ai-reviews.ts` | `aiReviews` | AI code review results | | |
21 | +| `ai-review-rules.ts` | `aiReviewRules` | AI review rule configurations | | |
22 | +| `developer-metrics.ts` | `developerMetrics` | PR metrics, cycle time | | |
23 | +| `branch-protection.ts` | `branchProtection`, `reviewRequirements`, `requiredStatusChecks` | Branch rules | | |
24 | +| `webhooks.ts` | `webhooks` | Outbound webhook configs | | |
25 | +| `automations.ts` | `automations` | Workflow automation rules | | |
26 | +| `slack-integration.ts` | `slackIntegration` | Slack notification configs | | |
27 | +| `organizations.ts` | `organizations` | Organizations | | |
28 | +| `teams.ts` | `teams` | Team membership | | |
29 | +| `roles.ts` | `roles` | Role-based access control | | |
30 | +| `activity.ts` | `activity` | Activity/audit log | | |
31 | +| `security.ts` | `security` | Security scan results | | |
32 | +| `security-policies.ts` | `securityPolicies` | Security policy definitions | | |
33 | +| `sso.ts` | `sso` | SSO/OIDC configuration | | |
34 | +| `wiki.ts` | `wiki` | Repository wiki pages | | |
35 | +| `deploy-keys.ts` | `deployKeys` | SSH deploy keys | | |
36 | +| `path-permissions.ts` | `pathPermissions` | File-level access control | | |
37 | +| `pr-states.ts` | `prStates` | Custom PR states | | |
38 | +| `issue-statuses.ts` | `issueStatuses` | Custom issue statuses | | |
39 | +| `custom-fields.ts` | `customFields` | Custom issue/project fields | | |
40 | +| `auto-merge-rules.ts` | `autoMergeRules` | Auto-merge configuration | | |
41 | +| `external-ci.ts` | `externalCIConfigs`, `externalBuilds` | External CI integration | | |
42 | +| `review-rules.ts` | `reviewRules` | Review requirements and reviewer routing | | |
43 | +| `integrations.ts` | `integrations` | Code quality, dashboard, deployment integrations | | |
44 | +| `packages.ts` | `packages` | Package registry metadata | | |
45 | +| `inbox-sections.ts` | `inboxSections` | Custom inbox sections | | |
46 | +| `notification-preferences.ts` | `notificationPreferences` | Per-user notification settings | | |
47 | +| `system-config.ts` | `systemConfig` | System-wide configuration | | |
9 | 48 | |
10 | -- **users**: Stores user accounts, hashed passwords, and profile info. | |
11 | -- **repositories**: Stores repository metadata (owner, name, visibility). | |
12 | -- **pull_requests**: Stores PR data, status, and description. | |
13 | -- **sessions**: Stores active login sessions (if using database sessions). | |
49 | +## Relationships | |
50 | + | |
51 | +Key relationships between tables: | |
52 | +- `repositories` → `users` (owner) | |
53 | +- `pullRequests` → `repositories` (repo) + `users` (author) | |
54 | +- `prStacks` → `repositories` + `prStackEntries` → `pullRequests` | |
55 | +- `mergeQueue` → `pullRequests` + `repositories` | |
56 | +- `workflowRuns` → `workflows` + `repositories` | |
57 | +- `issues` → `repositories` + `users` | |
58 | +- `projects` → `repositories` | |
14 | 59 | |
15 | 60 | ## Migrations |
16 | 61 | |
| @@ -26,7 +71,20 @@ npm run db:generate | ||
26 | 71 | npm run db:migrate |
27 | 72 | ``` |
28 | 73 | |
29 | -**Push schema (Prototyping):** | |
74 | +**Push schema (prototyping — no migration files):** | |
30 | 75 | ```bash |
31 | 76 | npm run db:push |
32 | 77 | ``` |
78 | + | |
79 | +**Open Drizzle Studio GUI:** | |
80 | +```bash | |
81 | +npm run db:studio | |
82 | +``` | |
83 | + | |
84 | +## Database Support | |
85 | + | |
86 | +| Driver | Use Case | Notes | | |
87 | +|--------|----------|-------| | |
88 | +| `postgres` | Production | Required. All schemas use `pgTable` | | |
89 | +| `sqlite` | Development | Simple local setup | | |
90 | +| `turso` | Edge (Vercel) | LibSQL for serverless deployments | | |
33 | 91 | |
▼docs-site/src/content/docs/development/local-dev-setup.md
+73-19
| @@ -5,48 +5,102 @@ description: "Set up a productive local dev environment for OpenCodeHub." | ||
5 | 5 | |
6 | 6 | ## Prerequisites |
7 | 7 | |
8 | -- Node.js 18+ (project `engines` specify Node 20+ for runtime) | |
9 | -- Git 2.30+ | |
10 | -- PostgreSQL/MySQL/SQLite (PostgreSQL recommended) | |
8 | +- **Node.js** 20+ (or Bun 1.0+) | |
9 | +- **Git** 2.30+ | |
10 | +- **PostgreSQL** 14+ (recommended) or SQLite (for quick setup) | |
11 | +- **Redis** 7+ (optional for local dev, required for background workers) | |
11 | 12 | |
12 | -## Install dependencies | |
13 | +## Quick Setup | |
13 | 14 | |
14 | 15 | ```bash |
15 | -npm install | |
16 | -``` | |
16 | +# Clone the repository | |
17 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
18 | +cd OpencodeHub | |
17 | 19 | |
18 | -## Environment | |
20 | +# Install dependencies | |
21 | +npm install | |
19 | 22 | |
20 | -```bash | |
23 | +# Copy environment config | |
21 | 24 | cp .env.example .env |
22 | -``` | |
23 | 25 | |
24 | -Set required values like `JWT_SECRET`, `SESSION_SECRET`, `INTERNAL_HOOK_SECRET`, and `SITE_URL`. | |
26 | +# Edit .env with your settings (at minimum, set the secrets) | |
27 | +# Generate secrets with: openssl rand -base64 32 | |
28 | +``` | |
25 | 29 | |
26 | 30 | ## Database |
27 | 31 | |
28 | 32 | ```bash |
33 | +# Push schema to database (creates tables) | |
29 | 34 | npm run db:push |
35 | + | |
36 | +# Or generate and run migrations | |
37 | +npm run db:generate | |
38 | +npm run db:migrate | |
39 | + | |
40 | +# Open Drizzle Studio GUI | |
41 | +npm run db:studio | |
42 | +``` | |
43 | + | |
44 | +For SQLite (simplest setup), set in `.env`: | |
45 | +``` | |
46 | +DATABASE_DRIVER=sqlite | |
47 | +DATABASE_URL=file:./data/opencodehub.db | |
30 | 48 | ``` |
31 | 49 | |
32 | -## Seed admin user | |
50 | +## Seed Admin User | |
33 | 51 | |
34 | 52 | ```bash |
35 | 53 | bun run scripts/seed-admin.ts |
36 | 54 | ``` |
37 | 55 | |
38 | -## Run dev server | |
56 | +## Start Development | |
39 | 57 | |
40 | 58 | ```bash |
59 | +# Main app (Web UI + API + Git HTTP) | |
41 | 60 | npm run dev |
61 | + | |
62 | +# SSH Git server (optional, for ssh:// clone/push) | |
63 | +npm run git:start | |
64 | + | |
65 | +# Background worker (optional, for webhooks/notifications) | |
66 | +npm run worker:start | |
67 | + | |
68 | +# CI runner (optional, for pipeline execution) | |
69 | +npm run runner:start | |
70 | +``` | |
71 | + | |
72 | +The dev server runs at `http://localhost:4321`. | |
73 | + | |
74 | +## Useful Scripts | |
75 | + | |
76 | +| Command | Purpose | | |
77 | +|---------|---------| | |
78 | +| `npm run dev` | Start Astro dev server | | |
79 | +| `npm run build` | Production build | | |
80 | +| `npm run test` | Run Vitest test suite | | |
81 | +| `npm run test:coverage` | Run tests with coverage | | |
82 | +| `npm run lint` | Run Astro check | | |
83 | +| `npm run typecheck` | TypeScript type checking | | |
84 | +| `npm run db:push` | Push schema to database | | |
85 | +| `npm run db:generate` | Generate Drizzle migrations | | |
86 | +| `npm run db:migrate` | Run migrations | | |
87 | +| `npm run db:studio` | Open Drizzle Studio | | |
88 | +| `npm run git:start` | Start SSH git server | | |
89 | +| `npm run worker:start` | Start background worker | | |
90 | +| `npm run runner:start` | Start CI runner | | |
91 | + | |
92 | +## Docker Compose (Alternative) | |
93 | + | |
94 | +For a full stack with PostgreSQL and Redis: | |
95 | + | |
96 | +```bash | |
97 | +docker-compose up -d | |
98 | +docker-compose exec app bun run scripts/seed-admin.ts | |
42 | 99 | ``` |
43 | 100 | |
44 | -Open the app at `http://localhost:3000`. | |
101 | +This starts the app on port 4321 and SSH on port 2222. | |
45 | 102 | |
46 | -## Useful scripts | |
103 | +## IDE Setup | |
47 | 104 | |
48 | -- `npm run db:generate` | |
49 | -- `npm run db:migrate` | |
50 | -- `npm run db:studio` | |
51 | -- `npm run lint` | |
52 | -- `npm run test` | |
105 | +- **VS Code**: Install the Astro extension for syntax highlighting | |
106 | +- **Recommended extensions**: Tailwind CSS IntelliSense, ESLint | |
53 | 107 | |
▼docs-site/src/content/docs/development/release-gates.md
+21-0
| @@ -0,0 +1,21 @@ | ||
1 | +# Release Gates | |
2 | + | |
3 | +This document outlines the required gates that must be passed before any code can be merged into the default branch. | |
4 | + | |
5 | +## 1. Continuous Integration (CI) | |
6 | +All pull requests must pass the following CI checks: | |
7 | +- **Linting**: `npm run lint` must exit with 0. | |
8 | +- **Typechecking**: `npm run typecheck` must exit with 0. | |
9 | +- **Tests**: Unit, integration, and E2E tests (`npm run test`) must all pass. | |
10 | + | |
11 | +## 2. Security Scans | |
12 | +- **Secret Scanning**: Commits must be scanned for leaked secrets. | |
13 | +- **Dependency Audit**: `npm audit` must not report vulnerabilities above the defined threshold. | |
14 | +- **Authorization Regression**: Core RBAC and permissions tests must pass. | |
15 | + | |
16 | +## 3. Documentation Parity | |
17 | +Any feature addition must include updates to both documentation stacks: | |
18 | +- The Markdown docs in `docs/` | |
19 | +- The Starlight site in `docs-site/src/content/docs/` | |
20 | + | |
21 | +Features will not be merged if their documentation is missing or incomplete. | |
0 | 22 | |
▼docs-site/src/content/docs/features/ai-review.md
+88-459
| @@ -2,67 +2,60 @@ | ||
2 | 2 | title: "AI Code Review" |
3 | 3 | --- |
4 | 4 | |
5 | -> Get instant, intelligent code reviews powered by GPT-4 or Claude | |
6 | - | |
7 | 5 | 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. |
8 | 6 | |
9 | -## 📖 Table of Contents | |
10 | - | |
11 | -- [Overview](#overview) | |
12 | -- [Setup](#setup) | |
13 | - - [Configure AI Provider](#configure-ai-provider) | |
14 | - - [Enable for Repository](#enable-for-repository) | |
15 | -- [Using AI Review](#using-ai-review) | |
16 | -- [Understanding Review Output](#understanding-review-output) | |
17 | -- [Customizing Reviews](#customizing-reviews) | |
18 | -- [Cost Considerations](#cost-considerations) | |
19 | -- [Best Practices](#best-practices) | |
20 | -- [Troubleshooting](#troubleshooting) | |
7 | +## Supported Providers | |
21 | 8 | |
9 | +OpenCodeHub supports 10+ AI providers. Configure one or more in your `.env` file: | |
22 | 10 | |
23 | -## Overview | |
11 | +| Provider | Models | API Key Env Var | | |
12 | +|----------|--------|-----------------| | |
13 | +| **OpenAI** | GPT-4, GPT-4o, GPT-4 Turbo | `OPENAI_API_KEY` | | |
14 | +| **Anthropic** | Claude 3.5 Sonnet, Claude 3 Opus | `ANTHROPIC_API_KEY` | | |
15 | +| **Google** | Gemini 1.5 Pro, Gemini Flash | `GOOGLE_AI_API_KEY` | | |
16 | +| **Groq** | Llama 3, Mixtral (fast inference) | `GROQ_API_KEY` | | |
17 | +| **OpenRouter** | Multi-model gateway | `OPENROUTER_API_KEY` | | |
18 | +| **Together** | Llama, Mixtral, CodeLlama | `TOGETHER_API_KEY` | | |
19 | +| **Bytez** | Various models | `BYTEZ_API_KEY` | | |
20 | +| **Ollama** | Any local model (Llama, CodeLlama, etc.) | No key needed | | |
21 | +| **External Agent** | Custom webhook | `EXTERNAL_AGENT_WEBHOOK_URL` | | |
24 | 22 | |
25 | -### What AI Review Catches | |
23 | +:::tip[Recommended] | |
24 | +For best results, use **GPT-4o** or **Claude 3.5 Sonnet**. For fast/cheap reviews, use **Groq** or **Ollama** with a local model. | |
25 | +::: | |
26 | 26 | |
27 | -🔒 **Security Vulnerabilities** | |
27 | +## What AI Review Catches | |
28 | 28 | |
29 | -- SQL injection risks | |
30 | -- XSS (Cross-Site Scripting) vectors | |
31 | -- Authentication bypasses | |
32 | -- Secrets/credentials in code | |
29 | +**Security Vulnerabilities** | |
30 | +- SQL injection, XSS, CSRF | |
31 | +- Authentication/authorization bypasses | |
32 | +- Exposed secrets or credentials | |
33 | 33 | - Insecure dependencies |
34 | 34 | |
35 | -⚡ **Performance Issues** | |
36 | - | |
35 | +**Performance Issues** | |
37 | 36 | - N+1 database queries |
38 | -- Inefficient algorithms (O(n²) → O(n log n)) | |
37 | +- Inefficient algorithms | |
39 | 38 | - Memory leaks |
40 | -- Blocking operations in async code | |
41 | 39 | - Missing indexes |
42 | 40 | |
43 | -📝 **Code Quality** | |
44 | - | |
41 | +**Code Quality** | |
45 | 42 | - Best practice violations |
46 | 43 | - Code smells (long functions, deep nesting) |
47 | 44 | - Incomplete error handling |
48 | 45 | - Missing input validation |
49 | -- Poor naming conventions | |
50 | - | |
51 | -🐛 **Potential Bugs** | |
52 | 46 | |
47 | +**Potential Bugs** | |
53 | 48 | - Null/undefined pointer exceptions |
54 | 49 | - Race conditions |
55 | 50 | - Off-by-one errors |
56 | -- Logic flaws | |
57 | 51 | - Type mismatches |
58 | 52 | |
59 | -### How It Works | |
53 | +## How It Works | |
60 | 54 | |
61 | 55 | ``` |
62 | 56 | 1. Developer opens/updates PR |
63 | 57 | ↓ |
64 | -2. OpenCodeHub sends diff to AI | |
58 | +2. OpenCodeHub sends diff to AI provider | |
65 | 59 | ↓ |
66 | 60 | 3. AI analyzes code changes |
67 | 61 | ↓ |
| @@ -71,526 +64,151 @@ AI Code Review uses advanced language models to automatically review your pull r | ||
71 | 64 | 5. Developer addresses feedback |
72 | 65 | ``` |
73 | 66 | |
74 | -**Privacy note:** Code is sent to AI provider (OpenAI/Anthropic). Use on-premise models for sensitive code. | |
75 | - | |
67 | +**Privacy note:** Code is sent to the configured AI provider. Use Ollama or the external agent for sensitive codebases. | |
76 | 68 | |
77 | 69 | ## Setup |
78 | 70 | |
79 | -### Configure AI Provider | |
80 | - | |
81 | -**Option 1: OpenAI (GPT-4)** | |
82 | - | |
83 | -1. Get API key from [platform.openai.com](https://platform.openai.com/api-keys) | |
84 | - | |
85 | -2. Add to `.env`: | |
71 | +### Step 1: Configure AI Provider | |
86 | 72 | |
73 | +**OpenAI:** | |
87 | 74 | ```bash |
88 | - AI_PROVIDER=openai | |
89 | - OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx | |
90 | - OPENAI_MODEL=gpt-4-turbo-preview # or gpt-4, gpt-3.5-turbo | |
75 | +OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx | |
91 | 76 | ``` |
92 | 77 | |
93 | -3. Restart OpenCodeHub | |
94 | - | |
95 | -**Option 2: Anthropic (Claude)** | |
96 | - | |
97 | -1. Get API key from [console.anthropic.com](https://console.anthropic.com/) | |
98 | - | |
99 | -2. Add to `.env`: | |
100 | - | |
78 | +**Anthropic:** | |
101 | 79 | ```bash |
102 | - AI_PROVIDER=anthropic | |
103 | - ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx | |
104 | - ANTHROPIC_MODEL=claude-3-opus-20240229 # or claude-3-sonnet | |
80 | +ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx | |
105 | 81 | ``` |
106 | 82 | |
107 | -3. Restart OpenCodeHub | |
108 | - | |
109 | -**Option 3: Azure OpenAI** | |
110 | - | |
83 | +**Google Gemini:** | |
111 | 84 | ```bash |
112 | -AI_PROVIDER=azure-openai | |
113 | -AZURE_OPENAI_API_KEY=xxxxx | |
114 | -AZURE_OPENAI_ENDPOINT=https://yourinstance.openai.azure.com/ | |
115 | -AZURE_OPENAI_DEPLOYMENT=your-deployment-name | |
85 | +GOOGLE_AI_API_KEY=xxxxxxxxxxxxx | |
116 | 86 | ``` |
117 | 87 | |
118 | -**Option 4: Self-Hosted (Coming Soon)** | |
119 | - | |
120 | -OpenCodeHub will support local models via Ollama: | |
88 | +**Groq (fast, cheap):** | |
89 | +```bash | |
90 | +GROQ_API_KEY=gsk_xxxxxxxxxxxxx | |
91 | +``` | |
121 | 92 | |
93 | +**Ollama (local, private):** | |
122 | 94 | ```bash |
123 | -AI_PROVIDER=ollama | |
124 | -OLLAMA_ENDPOINT=http://localhost:11434 | |
125 | -OLLAMA_MODEL=codellama:13b | |
95 | +# No env var needed — configure in Settings → AI | |
96 | +# Just install Ollama: https://ollama.com | |
97 | +# Pull a model: ollama pull codellama:13b | |
126 | 98 | ``` |
127 | 99 | |
128 | -### Enable for Repository | |
100 | +**Multiple providers:** Configure all keys. The system picks the best available provider. | |
129 | 101 | |
130 | -**Via Web UI:** | |
102 | +### Step 2: Enable for Repository | |
131 | 103 | |
132 | -1. Navigate to **Repository → Settings → AI Review** | |
133 | -2. Toggle **"Enable AI Code Review"** | |
134 | -3. Configure settings: | |
135 | - - **Trigger**: `On PR open`, `On push`, or `Manual only` | |
136 | - - **Auto-comment**: Post comments automatically | |
137 | - - **Severity threshold**: `All`, `Warning+`, or `Critical only` | |
138 | - - **Max files**: Limit review to N files (cost control) | |
104 | +**Via Web UI:** | |
105 | +1. Go to **Repository → Settings → AI Review** | |
106 | +2. Toggle **Enable AI Code Review** | |
107 | +3. Configure trigger, severity threshold, and max files | |
139 | 108 | 4. Click **Save** |
140 | 109 | |
141 | 110 | **Via API:** |
142 | - | |
143 | 111 | ```bash |
144 | -curl -X PATCH https://git.yourcompany.com/api/repos/123/ai-review \ | |
112 | +curl -X PATCH https://git.yourcompany.com/api/repos/OWNER/REPO/settings \ | |
145 | 113 | -H "Authorization: Bearer $TOKEN" \ |
146 | - -d '{ | |
147 | - "enabled": true, | |
148 | - "trigger": "on_pr_open", | |
149 | - "auto_comment": true, | |
150 | - "severity_threshold": "warning" | |
151 | - }' | |
114 | + -d '{ "aiReview": { "enabled": true } }' | |
152 | 115 | ``` |
153 | 116 | |
154 | 117 | **Via CLI:** |
155 | - | |
156 | 118 | ```bash |
157 | -och repo config ai-review --enable \ | |
158 | - --trigger on_pr_open \ | |
159 | - --auto-comment | |
119 | +och repo config ai-review --enable | |
160 | 120 | ``` |
161 | 121 | |
162 | - | |
163 | 122 | ## Using AI Review |
164 | 123 | |
165 | 124 | ### Automatic Reviews |
166 | 125 | |
167 | -If enabled, AI reviews trigger automatically: | |
168 | - | |
169 | -```bash | |
170 | -# 1. Create PR | |
171 | -git push origin feature-branch | |
172 | -# → AI review starts automatically | |
173 | - | |
174 | -# 2. Update PR | |
175 | -git push origin feature-branch | |
176 | -# → AI re-reviews changed files only | |
177 | -``` | |
178 | - | |
179 | -You'll see: | |
180 | - | |
181 | -- ✅ **Status check**: "AI Review: In Progress" → "AI Review: Complete" | |
182 | -- 💬 **Inline comments**: On specific lines with issues | |
183 | -- 📊 **Summary comment**: Overall assessment | |
126 | +When enabled, AI reviews trigger automatically when PRs are opened or updated. | |
184 | 127 | |
185 | 128 | ### Manual Reviews |
186 | 129 | |
187 | -Request AI review on-demand: | |
188 | - | |
189 | -**Via Web UI:** | |
190 | - | |
191 | -1. Open PR page | |
192 | -2. Click **"Request AI Review"** button | |
193 | -3. Wait ~30-60 seconds | |
194 | -4. Review appears as comments | |
195 | - | |
196 | -**Via CLI:** | |
130 | +**Web UI:** Open PR page → Click "Request AI Review" | |
197 | 131 | |
132 | +**CLI:** | |
198 | 133 | ```bash |
199 | -# Review specific PR | |
200 | -och review ai 125 | |
201 | - | |
202 | -# Wait for the review to complete | |
203 | -och review ai 125 --wait | |
134 | +och review ai <pr-number> | |
204 | 135 | ``` |
205 | 136 | |
206 | -**Via API:** | |
207 | - | |
137 | +**API:** | |
208 | 138 | ```bash |
209 | 139 | curl -X POST https://git.yourcompany.com/api/repos/OWNER/REPO/pulls/125/ai-review \ |
210 | 140 | -H "Authorization: Bearer $TOKEN" |
211 | 141 | ``` |
212 | 142 | |
213 | - | |
214 | 143 | ## Understanding Review Output |
215 | 144 | |
216 | 145 | ### Severity Levels |
217 | 146 | |
218 | -AI assigns severity to each finding: | |
219 | - | |
220 | -| Severity | Icon | Meaning | Example | | |
221 | -| --------------- | ---------- | -------------------------------------- | ----------------------------------------- | | |
222 | -| 🔴 **CRITICAL** | Must fix | Security vulnerability, data loss risk | SQL injection, exposed API key | | |
223 | -| 🟡 **WARNING** | Should fix | Performance issue, bad practice | N+1 query, missing error handling | | |
224 | -| 🔵 **INFO** | Consider | Suggestion, refactoring opportunity | Better variable name, code simplification | | |
225 | - | |
226 | -### Example Review Comment | |
227 | - | |
228 | -````markdown | |
229 | -🔴 CRITICAL: SQL Injection Vulnerability | |
230 | - | |
231 | -**File:** `src/api/users.ts` | |
232 | -**Line:** 45 | |
233 | - | |
234 | -**Issue:** | |
235 | -User input is directly concatenated into SQL query without sanitization. | |
236 | - | |
237 | -**Current Code:** | |
238 | - | |
239 | -```typescript | |
240 | -const query = `SELECT * FROM users WHERE email = '${userEmail}'`; | |
241 | -``` | |
242 | -```` | |
243 | - | |
244 | -**Risk:** | |
245 | -An attacker could inject malicious SQL: | |
246 | - | |
247 | -``` | |
248 | -userEmail = "' OR '1'='1" | |
249 | -→ Query becomes: SELECT * FROM users WHERE email = '' OR '1'='1' | |
250 | -→ Returns ALL users (authentication bypass) | |
251 | -``` | |
252 | - | |
253 | -**Fix:** | |
254 | -Use parameterized queries or ORM: | |
255 | - | |
256 | -```typescript | |
257 | -const query = `SELECT * FROM users WHERE email = ?`; | |
258 | -const results = await db.query(query, [userEmail]); | |
259 | - | |
260 | -// Or with ORM: | |
261 | -const user = await db.users.findOne({ email: userEmail }); | |
262 | -``` | |
263 | - | |
264 | -**References:** | |
265 | - | |
266 | -- [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) | |
267 | -- [Node.js SQL Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) | |
268 | - | |
269 | -```` | |
147 | +| Severity | Meaning | Example | | |
148 | +|----------|---------|---------| | |
149 | +| **CRITICAL** | Must fix — security/data loss | SQL injection, exposed key | | |
150 | +| **WARNING** | Should fix — perf/best practice | N+1 query, missing error handling | | |
151 | +| **INFO** | Consider — suggestion | Better variable name | | |
270 | 152 | |
271 | 153 | ### Summary Comment |
272 | 154 | |
273 | -AI posts an overall summary: | |
274 | - | |
275 | -```markdown | |
276 | -## 🤖 AI Code Review Summary | |
155 | +AI posts an overall summary with statistics, critical issues, and recommendations. | |
277 | 156 | |
278 | -**Overall Assessment:** ⚠️ Needs Attention | |
157 | +### Custom Rules | |
279 | 158 | |
280 | -### Statistics | |
281 | -- Files reviewed: 8 | |
282 | -- Critical issues: 2 | |
283 | -- Warnings: 5 | |
284 | -- Info: 3 | |
285 | - | |
286 | -### Critical Issues | |
287 | -1. 🔴 SQL Injection in `src/api/users.ts:45` | |
288 | -2. 🔴 Hardcoded API key in `src/config.ts:12` | |
289 | - | |
290 | -### Recommendations | |
291 | -1. Add input validation to all API endpoints | |
292 | -2. Move secrets to environment variables | |
293 | -3. Consider adding integration tests for auth flows | |
294 | - | |
295 | -### Positive Notes | |
296 | -- ✅ Good error handling in payment service | |
297 | -- ✅ Well-structured database migrations | |
298 | -- ✅ Comprehensive TypeScript types | |
299 | - | |
300 | -**Estimated fix time:** 2-3 hours | |
301 | -```` | |
302 | - | |
303 | - | |
304 | -## Customizing Reviews | |
305 | - | |
306 | -### Review Rules | |
307 | - | |
308 | -Create `.opencodehub/ai-review.yml` in your repository: | |
159 | +Create `.opencodehub/ai-review.yml` in your repo: | |
309 | 160 | |
310 | 161 | ```yaml |
311 | -# AI Review Configuration | |
312 | - | |
313 | -# Which files to review | |
314 | 162 | include: |
315 | 163 | - "src/**/*.ts" |
316 | - - "src/**/*.tsx" | |
317 | - - "lib/**/*.js" | |
318 | - | |
319 | -# Which files to skip | |
320 | 164 | exclude: |
321 | 165 | - "**/*.test.ts" |
322 | - - "**/*.spec.ts" | |
323 | - - "**/generated/**" | |
324 | 166 | - "dist/**" |
325 | 167 | |
326 | -# Focus areas (weighted 1-10) | |
327 | 168 | focus: |
328 | - security: 10 # Highest priority | |
169 | + security: 10 | |
329 | 170 | performance: 8 |
330 | 171 | bugs: 8 |
331 | 172 | code_quality: 5 |
332 | - documentation: 3 | |
333 | - | |
334 | -# Severity thresholds | |
335 | -report: | |
336 | - critical: always | |
337 | - warning: always | |
338 | - info: on_request # Only if --verbose flag used | |
339 | 173 | |
340 | -# Custom rules | |
341 | 174 | custom_rules: |
342 | 175 | - name: "No console.log in production" |
343 | - pattern: 'console\\.log\\(' | |
176 | + pattern: 'console\.log\(' | |
344 | 177 | severity: warning |
345 | 178 | message: "Remove console.log before merging" |
346 | - | |
347 | - - name: "Require error handling" | |
348 | - pattern: 'await.*\\(.*\\)' | |
349 | - check: must_have_try_catch | |
350 | - severity: warning | |
351 | - | |
352 | -# AI instructions | |
353 | -instructions: > | |
354 | - Pay special attention to: | |
355 | - 1. Authentication and authorization logic | |
356 | - 2. Database query efficiency | |
357 | - 3. Input validation | |
358 | - 4. Proper error handling | |
359 | - | |
360 | - Our coding standards: | |
361 | - - TypeScript strict mode | |
362 | - - Functional programming preferred | |
363 | - - Maximum function length: 50 lines | |
364 | - - Maximum cyclomatic complexity: 10 | |
365 | 179 | ``` |
366 | 180 | |
367 | -### Custom Prompts | |
368 | - | |
369 | -Use **Settings → AI Review Rules** to add repository‑specific prompts and checks. Rules are applied automatically when AI reviews run. | |
370 | - | |
371 | -### Language-Specific Reviews | |
372 | - | |
373 | -AI automatically adapts to programming language: | |
374 | - | |
375 | -- **TypeScript/JavaScript**: Type safety, async/await, React best practices | |
376 | -- **Python**: PEP 8, type hints, Django/Flask patterns | |
377 | -- **Go**: Go idioms, error handling, concurrency | |
378 | -- **Rust**: Borrow checker, unsafe code, idiomatic Rust | |
379 | -- **Java**: Design patterns, Spring best practices, thread safety | |
380 | - | |
181 | +Or use **Settings → AI Review Rules** in the web UI. | |
381 | 182 | |
382 | 183 | ## Cost Considerations |
383 | 184 | |
384 | -### Pricing | |
385 | - | |
386 | -AI review costs depend on provider and model: | |
387 | - | |
388 | -**OpenAI (GPT-4 Turbo):** | |
389 | - | |
390 | -- ~$0.01 per 1K tokens (input) | |
391 | -- ~$0.03 per 1K tokens (output) | |
392 | -- **Typical PR**: 500-2000 tokens = **$0.02-0.10 per review** | |
393 | - | |
394 | -**Anthropic (Claude 3 Opus):** | |
395 | - | |
396 | -- ~$0.015 per 1K tokens (input) | |
397 | -- ~$0.075 per 1K tokens (output) | |
398 | -- **Typical PR**: **$0.03-0.15 per review** | |
399 | - | |
400 | -**Example monthly cost:** | |
401 | - | |
402 | -- 50 PRs/month × $0.05/review = **$2.50/month** | |
403 | -- 500 PRs/month × $0.05/review = **$25/month** | |
404 | - | |
405 | -### Cost Optimization | |
406 | - | |
407 | -To reduce cost: | |
408 | - | |
409 | -- Keep PRs small and focused. | |
410 | -- Avoid very large diffs in a single review. | |
185 | +| Provider | Approx Cost per Review | | |
186 | +|----------|----------------------| | |
187 | +| Groq | Free (rate limited) | | |
188 | +| Ollama | Free (local) | | |
189 | +| OpenAI GPT-4o | ~$0.02-0.10 | | |
190 | +| Anthropic Claude | ~$0.03-0.15 | | |
191 | +| Google Gemini | ~$0.01-0.05 | | |
411 | 192 | |
412 | -**3. Skip test files:** | |
413 | - | |
414 | -```yaml | |
415 | -exclude: | |
416 | - - "**/*.test.*" | |
417 | - - "**/*.spec.*" | |
418 | -``` | |
419 | - | |
420 | -**4. Review only on-request:** | |
421 | - | |
422 | -```yaml | |
423 | -trigger: manual # Don't auto-review every push | |
424 | -``` | |
425 | - | |
426 | -**5. Use severity threshold:** | |
427 | - | |
428 | -```yaml | |
429 | -severity_threshold: warning # Skip "info" level comments | |
430 | -``` | |
431 | - | |
432 | - | |
433 | -## Best Practices | |
434 | - | |
435 | -### 💡 When to Use AI Review | |
436 | - | |
437 | -**✅ Good Use Cases:** | |
438 | - | |
439 | -- Security-sensitive code (auth, payments) | |
440 | -- Complex algorithms | |
441 | -- Database queries | |
442 | -- API endpoints | |
443 | -- Third-party integrations | |
444 | - | |
445 | -**❌ Less Useful:** | |
446 | - | |
447 | -- Generated code | |
448 | -- Configuration files | |
449 | -- Simple refactoring | |
450 | -- Documentation-only changes | |
451 | - | |
452 | -### 💡 Responding to AI Feedback | |
453 | - | |
454 | -**1. Don't auto-fix everything** | |
455 | - | |
456 | -- AI can be wrong | |
457 | -- Context matters | |
458 | -- Use judgment | |
459 | - | |
460 | -**2. Engage in discussion** | |
461 | - | |
462 | -```markdown | |
463 | -@ai-review That's a good catch, but in this case we're using a | |
464 | -trusted source and the data is already sanitized earlier in the pipeline. | |
465 | - | |
466 | -See line 23 where we validate input. | |
467 | -``` | |
468 | - | |
469 | -**3. Track false positives** | |
470 | - | |
471 | -```yaml | |
472 | -# .opencodehub/ai-review.yml | |
473 | -ignore: | |
474 | - - file: src/utils/legacy.ts | |
475 | - reason: "Legacy code, requires major refactor" | |
476 | - until: 2024-12-31 | |
477 | -``` | |
478 | - | |
479 | -### 💡 Combining with Human Review | |
480 | - | |
481 | -AI complements, doesn't replace humans: | |
482 | - | |
483 | -``` | |
484 | -1. AI reviews first (catches obvious issues) | |
485 | -2. Developer fixes critical/warnings | |
486 | -3. Human reviewer approves | |
487 | -``` | |
488 | - | |
489 | -This workflow: | |
490 | - | |
491 | -- Reduces human reviewer burden | |
492 | -- Catches more bugs | |
493 | -- Faster feedback cycles | |
494 | - | |
193 | +**Cost optimization:** | |
194 | +- Keep PRs small and focused | |
195 | +- Skip test files in review config | |
196 | +- Use severity threshold to filter info-level comments | |
197 | +- Use Groq or Ollama for routine reviews, GPT-4/Claude for critical code | |
495 | 198 | |
496 | 199 | ## Troubleshooting |
497 | 200 | |
498 | -### "AI review taking too long" | |
499 | - | |
500 | -**Cause:** Large PR (many files/lines). | |
501 | - | |
502 | -**Solution:** | |
503 | - | |
504 | -```bash | |
505 | -# Check review status | |
506 | -och review status 125 | |
507 | - | |
508 | -# Retry the review | |
509 | -och review ai 125 | |
510 | -``` | |
511 | - | |
512 | -### "AI review failed" | |
513 | - | |
514 | -**Causes:** | |
515 | - | |
516 | -1. API key invalid/expired | |
517 | -2. Rate limit exceeded | |
518 | -3. Timeout (very large PR) | |
519 | - | |
520 | -**Solutions:** | |
201 | +**AI review taking too long:** Large PR (many files). Split into smaller PRs. | |
521 | 202 | |
522 | -```bash | |
523 | -# 1. Verify API key | |
524 | -echo $OPENAI_API_KEY | |
525 | - | |
526 | -# 2. Check rate limits | |
527 | -curl https://api.openai.com/v1/usage \ | |
528 | - -H "Authorization: Bearer $OPENAI_API_KEY" | |
529 | - | |
530 | -# 3. Split large PR or review manually. | |
531 | -``` | |
532 | - | |
533 | -### "Too many false positives" | |
534 | - | |
535 | -**Solution:** Use **Settings → AI Review Rules** to refine prompts and reduce false positives. | |
536 | - | |
537 | -### "Missing obvious bugs" | |
538 | - | |
539 | -**Cause:** AI missed context or edge case. | |
540 | - | |
541 | -**Solution:** Add custom instructions in **Settings → AI Review Rules** so reviews incorporate your conventions. | |
542 | - | |
543 | - | |
544 | -## Advanced: CI/CD Integration | |
545 | - | |
546 | -### Block merge on critical findings | |
203 | +**AI review failed:** Check API key is valid. Verify provider is configured in Settings → AI. | |
547 | 204 | |
548 | -```yaml | |
549 | -# .github/workflows/ai-review.yml | |
550 | -name: AI Code Review | |
551 | -on: [pull_request] | |
552 | - | |
553 | -jobs: | |
554 | - ai-review: | |
555 | - runs-on: ubuntu-latest | |
556 | - steps: | |
557 | - - name: Run AI Review | |
558 | - run: och review ai ${{ github.event.pull_request.number }} | |
559 | - env: | |
560 | - OCH_TOKEN: ${{ secrets.OCH_TOKEN }} | |
561 | - | |
562 | - - name: Check for critical issues | |
563 | - run: | | |
564 | - REVIEW=$(curl -s \ | |
565 | - -H "Authorization: Bearer $OCH_TOKEN" \ | |
566 | - https://git.yourcompany.com/api/repos/OWNER/REPO/pulls/${{ github.event.pull_request.number }}/ai-review/latest) | |
567 | - SEVERITY=$(echo "$REVIEW" | jq -r '.data.overallSeverity') | |
568 | - if [ "$SEVERITY" = "critical" ]; then | |
569 | - echo "❌ Critical issues found" | |
570 | - exit 1 | |
571 | - fi | |
572 | -``` | |
205 | +**Too many false positives:** Refine rules in Settings → AI Review Rules. Add custom instructions. | |
573 | 206 | |
207 | +**Missing obvious bugs:** Add custom instructions describing your conventions and patterns. | |
574 | 208 | |
575 | 209 | ## See Also |
576 | 210 | |
577 | -- [Stacked Pull Requests](stacked-prs.md) | |
578 | -- [Smart Merge Queue](merge-queue.md) | |
579 | -- [CI/CD Actions](ci-actions.md) | |
580 | - | |
581 | - | |
582 | -## Need Help? | |
583 | - | |
584 | -- 💬 [Discord Community](https://discord.gg/opencodehub) | |
585 | -- 🐛 [Report Issues](https://github.com/swadhinbiswas/OpencodeHub/issues) | |
586 | -- 📖 [Full Documentation](/) | |
211 | +- [Stacked PRs](/features/stacked-prs/) | |
212 | +- [Merge Queue](/features/merge-queue/) | |
213 | +- [CI/CD Actions](/features/ci-actions/) | |
214 | +- [Configuration Reference](/administration/configuration/#ai-providers) | |
587 | 215 | |
▼docs-site/src/content/docs/getting-started/first-repository.md
+73-32
| @@ -2,67 +2,108 @@ | ||
2 | 2 | title: "Create Your First Repository" |
3 | 3 | --- |
4 | 4 | |
5 | - | |
6 | 5 | This guide walks you through the complete lifecycle of a repository in OpenCodeHub. |
7 | 6 | |
8 | -## Step 1: Creation | |
9 | - | |
10 | -1. Click the **+** button in the top navbar and select **New Repository**. | |
11 | -2. **Owner**: Select yourself or an organization. | |
12 | -3. **Repository Name**: Enter `hello-world`. | |
13 | -4. **Description**: "My first project on OpenCodeHub". | |
14 | -5. **Visibility**: Select **Public**. | |
15 | -6. **Initialize**: Check **Initialize with README**. | |
7 | +## Step 1: Create the Repository | |
16 | 8 | |
17 | -Click **Create Repository**. You'll be taken to your new repository's home page. | |
9 | +1. Click the **+** button in the top navbar → **New Repository** | |
10 | +2. **Owner**: Select yourself or an organization | |
11 | +3. **Repository Name**: Enter `hello-world` | |
12 | +4. **Description**: "My first project on OpenCodeHub" | |
13 | +5. **Visibility**: Select **Public** | |
14 | +6. **Initialize**: Check **Initialize with README** | |
18 | 15 | |
19 | -## Step 2: Cloning | |
16 | +Click **Create Repository**. | |
20 | 17 | |
21 | -To work on your computer, you need to clone the repository. | |
18 | +## Step 2: Clone | |
22 | 19 | |
23 | 20 | ### SSH (Recommended) |
24 | -Ensure you have added your SSH key in **Settings > SSH Keys**. | |
21 | + | |
22 | +Add your SSH key in **Settings → SSH Keys** first. | |
25 | 23 | |
26 | 24 | ```bash |
27 | -git clone [email protected]:username/hello-world.git | |
25 | +git clone ssh://git@your-server:2222/username/hello-world.git | |
28 | 26 | ``` |
29 | 27 | |
30 | 28 | ### HTTPS |
29 | + | |
31 | 30 | ```bash |
32 | -git clone https://your-instance.com/username/hello-world.git | |
31 | +git clone https://git.yourcompany.com/username/hello-world.git | |
33 | 32 | ``` |
34 | 33 | |
35 | -## Step 3: Making Changes | |
34 | +For HTTPS, you'll need a Personal Access Token. Generate one at **Settings → Tokens**. | |
36 | 35 | |
37 | -Open the folder in your favorite editor (VS Code, etc.). | |
36 | +## Step 3: Make Changes | |
38 | 37 | |
39 | 38 | ```bash |
40 | 39 | cd hello-world |
41 | -``` | |
42 | 40 | |
43 | -Modify `README.md`: | |
44 | -```markdown | |
45 | -# Hello World | |
46 | -This is my first change! | |
47 | -``` | |
41 | +# Edit files | |
42 | +echo "# Hello World\n\nThis is my first change!" > README.md | |
48 | 43 | |
49 | -Commit the change: | |
50 | -```bash | |
44 | +# Commit | |
51 | 45 | git add README.md |
52 | 46 | git commit -m "Update README" |
53 | 47 | ``` |
54 | 48 | |
55 | -## Step 4: Pushing | |
56 | - | |
57 | -Push your changes back to OpenCodeHub: | |
49 | +## Step 4: Push | |
58 | 50 | |
59 | 51 | ```bash |
60 | 52 | git push origin main |
61 | 53 | ``` |
62 | -(Or use `och push` if you installed the CLI). | |
63 | 54 | |
64 | -## Step 5: Viewing Changes | |
55 | +Refresh the repository page to see your changes. | |
56 | + | |
57 | +## Step 5: Create a Pull Request | |
58 | + | |
59 | +1. Create a feature branch: | |
60 | +```bash | |
61 | +git checkout -b feat/add-feature | |
62 | +``` | |
63 | + | |
64 | +2. Make changes and push: | |
65 | +```bash | |
66 | +echo "New feature" > feature.txt | |
67 | +git add feature.txt | |
68 | +git commit -m "Add new feature" | |
69 | +git push -u origin feat/add-feature | |
70 | +``` | |
71 | + | |
72 | +3. In the web UI, go to **Pull Requests** → **New Pull Request** | |
73 | +4. Select `feat/add-feature` as source, `main` as target | |
74 | +5. Add a title and description | |
75 | +6. Click **Create Pull Request** | |
76 | + | |
77 | +## Step 6: Review and Merge | |
78 | + | |
79 | +1. Add reviewers on the PR page | |
80 | +2. Reviewers can approve or request changes | |
81 | +3. Once approved, click **Merge** or add to the **Merge Queue** | |
82 | + | |
83 | +## Step 7: Explore Features | |
84 | + | |
85 | +### Wiki | |
86 | +Go to the **Wiki** tab to create documentation for your project. | |
87 | + | |
88 | +### Issues | |
89 | +Go to **Issues** → **New Issue** to track bugs and feature requests. | |
90 | + | |
91 | +### Projects | |
92 | +Go to **Projects** to create a kanban board for task management. | |
93 | + | |
94 | +### Actions | |
95 | +Go to **Actions** to view CI/CD pipeline runs. Create `.github/workflows/*.yml` files to set up automated builds. | |
96 | + | |
97 | +### Settings | |
98 | +Go to **Settings** to configure: | |
99 | +- **Branch Protection** — Require reviews, status checks | |
100 | +- **Collaborators** — Add team members | |
101 | +- **Webhooks** — Integrations with external services | |
102 | +- **Deploy Keys** — SSH keys for automated deployments | |
65 | 103 | |
66 | -Refresh your repository page in the browser. You should see your updated `README.md` displayed and the latest commit listed. | |
104 | +## Next Steps | |
67 | 105 | |
68 | -Congratulations! You've successfully hosted code on OpenCodeHub. | |
106 | +- **[Stacked PRs](/features/stacked-prs/)** — Break large changes into reviewable stacks | |
107 | +- **[AI Code Review](/features/ai-review/)** — Get automated code feedback | |
108 | +- **[CI/CD](/features/ci-actions/)** — Set up automated pipelines | |
109 | +- **[CLI](/reference/cli-overview/)** — Manage everything from the terminal | |
69 | 110 | |
▼docs-site/src/content/docs/getting-started/installation.md
+78-37
| @@ -2,10 +2,9 @@ | ||
2 | 2 | title: "Installation Guide" |
3 | 3 | --- |
4 | 4 | |
5 | +OpenCodeHub is a self-hosted Git platform. You can run it via **Docker** (recommended) or **Node.js** (for development). | |
5 | 6 | |
6 | -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). | |
7 | - | |
8 | -## 📋 System Requirements | |
7 | +## System Requirements | |
9 | 8 | |
10 | 9 | | Component | Minimum | Recommended | |
11 | 10 | |-----------|---------|-------------| |
| @@ -14,53 +13,57 @@ OpenCodeHub allows you to host your own GitHub-like platform. You can run it via | ||
14 | 13 | | **Disk** | 10GB | 50GB SSD | |
15 | 14 | | **OS** | Linux (Ubuntu/Debian) | Linux | |
16 | 15 | |
17 | - | |
18 | -## ⚡ 1-Click Installation (Recommended) | |
19 | - | |
20 | -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`. | |
16 | +## 1-Click Installation (Recommended) | |
21 | 17 | |
22 | 18 | ```bash |
23 | 19 | curl -sSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash |
24 | 20 | ``` |
25 | 21 | |
26 | 22 | The script will: |
27 | -1. Ensure Docker and Docker Compose are installed. | |
28 | -2. Clone the latest stable release. | |
29 | -3. Automatically configure a unified `/data` mount point (`DATA_DIR`) for all your databases, git repos, and storage to easily enable painless backups. | |
30 | -4. Prompt you to create an initial Admin user account. | |
31 | - | |
32 | -### Manual Setup (Advanced) | |
23 | +1. Ensure Docker and Docker Compose are installed | |
24 | +2. Clone the latest stable release | |
25 | +3. Configure a unified `DATA_DIR` for databases, git repos, and storage | |
26 | +4. Prompt you to create an initial admin user | |
33 | 27 | |
34 | -If you prefer to configure Docker Compose manually: | |
28 | +## Manual Docker Setup | |
35 | 29 | |
36 | -1. Clone the repository: | |
37 | 30 | ```bash |
31 | +# Clone | |
38 | 32 | git clone https://github.com/swadhinbiswas/OpencodeHub.git |
39 | -cd OpenCodeHub | |
40 | -``` | |
33 | +cd OpencodeHub | |
41 | 34 | |
42 | -2. Configure environment: | |
43 | -```bash | |
35 | +# Configure | |
44 | 36 | cp .env.example .env |
45 | -``` | |
46 | -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. | |
37 | +# Edit .env — at minimum, change all secrets! | |
47 | 38 | |
48 | -3. Start services: | |
49 | -```bash | |
39 | +# Start | |
50 | 40 | docker-compose up -d |
41 | + | |
42 | +# Create admin user | |
43 | +docker-compose exec app bun run scripts/seed-admin.ts | |
51 | 44 | ``` |
52 | 45 | |
53 | -4. Initialize the database and admin user: | |
46 | +The app runs on **port 4321** (HTTP) and **port 2222** (SSH git). | |
47 | + | |
48 | +:::caution[Change Secrets] | |
49 | +Before going live, generate new secrets for all `*_SECRET` and `*_KEY` variables: | |
54 | 50 | ```bash |
55 | -bun run scripts/seed-admin.ts | |
51 | +openssl rand -base64 32 | |
56 | 52 | ``` |
53 | +::: | |
57 | 54 | |
55 | +## Docker Compose Services | |
58 | 56 | |
59 | -## 🛠 Nginx Reverse Proxy (SSL) | |
57 | +| Service | Port | Purpose | | |
58 | +|---------|------|---------| | |
59 | +| `app` | 4321, 2222 | Main application + SSH git | | |
60 | +| `postgres` | 5432 | PostgreSQL database | | |
61 | +| `redis` | 6379 | Redis cache/queues | | |
62 | +| `runner` | — | CI/CD Docker runner | | |
60 | 63 | |
61 | -In production, you **must** use HTTPS. Here is a standard Nginx configuration. | |
64 | +## Nginx Reverse Proxy (SSL) | |
65 | + | |
66 | +In production, use HTTPS. Standard Nginx config: | |
62 | 67 | |
63 | 68 | ```nginx |
64 | 69 | server { |
| @@ -80,7 +83,7 @@ server { | ||
80 | 83 | client_max_body_size 500M; |
81 | 84 | |
82 | 85 | location / { |
83 | - proxy_pass http://localhost:3000; | |
86 | + proxy_pass http://localhost:4321; | |
84 | 87 | proxy_set_header Host $host; |
85 | 88 | proxy_set_header X-Real-IP $remote_addr; |
86 | 89 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| @@ -89,21 +92,55 @@ server { | ||
89 | 92 | } |
90 | 93 | ``` |
91 | 94 | |
95 | +See [Nginx Deployment](/administration/deploy-nginx/) for the full config with WebSocket support and Git optimizations. | |
92 | 96 | |
93 | -## ✅ Production Checklist | |
97 | +## SSH Git Access | |
94 | 98 | |
95 | -Before going live to users: | |
99 | +The SSH git server starts automatically on port 2222. Clone with: | |
96 | 100 | |
97 | -- [ ] **Secrets Rotated**: Default secrets replaced with strong random strings. | |
98 | -- [ ] **HTTPS Enabled**: SSL certificate configured via Nginx/Caddy. | |
99 | -- [ ] **Rate Limiting**: `RATE_LIMIT_*` env vars adjusted for expected load. | |
100 | -- [ ] **Monitoring**: Grafana/Sentry configured for error tracking ([Guide](../administration/monitoring.md)). | |
101 | +```bash | |
102 | +git clone ssh://git@your-server:2222/owner/repo.git | |
103 | +``` | |
101 | 104 | |
105 | +The host key is auto-generated at `GIT_SSH_HOST_KEY` on first start. | |
102 | 106 | |
103 | -## Next Steps | |
107 | +## Node.js Setup (Development) | |
108 | + | |
109 | +```bash | |
110 | +# Install dependencies | |
111 | +npm install | |
112 | + | |
113 | +# Configure | |
114 | +cp .env.example .env | |
115 | + | |
116 | +# Push schema to database | |
117 | +npm run db:push | |
118 | + | |
119 | +# Create admin user | |
120 | +bun run scripts/seed-admin.ts | |
121 | + | |
122 | +# Start dev server | |
123 | +npm run dev | |
124 | +``` | |
125 | + | |
126 | +See [Local Dev Setup](/development/local-dev-setup/) for details. | |
104 | 127 | |
105 | -Now that you have OpenCodeHub up and running, let's look around: | |
128 | +## Production Checklist | |
129 | + | |
130 | +Before going live: | |
131 | + | |
132 | +- [ ] All secrets rotated (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, etc.) | |
133 | +- [ ] HTTPS enabled via Nginx/Caddy | |
134 | +- [ ] PostgreSQL used (not SQLite) | |
135 | +- [ ] Redis configured for distributed locking | |
136 | +- [ ] Rate limiting enabled (`RATE_LIMIT_ENABLED=true`) | |
137 | +- [ ] Email configured (SMTP) for notifications | |
138 | +- [ ] Storage backend configured (S3 recommended for multi-node) | |
139 | +- [ ] Backup strategy configured | |
140 | +- [ ] Monitoring set up (see [Monitoring](/administration/monitoring/)) | |
141 | + | |
142 | +## Next Steps | |
106 | 143 | |
107 | -👉 **[Quick Start Guide](quick-start.md)** | |
144 | +- **[Quick Start Guide](/getting-started/quick-start/)** — Navigate the dashboard and create your first repo | |
145 | +- **[Configuration Reference](/administration/configuration/)** — All environment variables | |
146 | +- **[Docker Deployment](/administration/deploy-docker/)** — Production Docker setup | |
108 | 147 | |
▼docs-site/src/content/docs/getting-started/quick-start.md
+94-26
| @@ -2,36 +2,103 @@ | ||
2 | 2 | title: "Quick Start Guide" |
3 | 3 | --- |
4 | 4 | |
5 | - | |
6 | -Welcome to OpenCodeHub! This guide will help you navigate the platform and start collaborating in minutes. | |
5 | +Welcome to OpenCodeHub! This guide will help you get started in minutes. | |
7 | 6 | |
8 | 7 | ## 1. Login |
9 | -Navigate to your instance URL (e.g., `http://localhost:3000`) and log in with the admin credentials you created during installation. | |
10 | - | |
11 | -## 2. Navigating the Dashboard | |
12 | -Your dashboard gives you a quick overview of: | |
13 | -- **Recent Repositories**: Projects you've worked on recently. | |
14 | -- **Your Pull Requests**: PRs requiring your attention. | |
15 | -- **Activity Feed**: What your team is doing. | |
16 | - | |
17 | -## 3. Create a Repository | |
18 | -1. Click the **+** (Plus) icon in the top right navigation bar. | |
19 | -2. Select **New Repository**. | |
20 | -3. Enter a **Repository Name** (e.g., `my-awesome-project`). | |
21 | -4. Choose Visibility: | |
22 | - - **Public**: Visible to everyone (if your instance allows it). | |
23 | - - **Private**: Visible only to you and collaborators. | |
24 | -5. Click **Create Repository**. | |
25 | - | |
26 | -## 4. Collaborate | |
27 | -1. **Invite Members**: Go to **Settings** > **Collaborators** to add team members. | |
28 | -2. **Create Issues**: Use the **Issues** tab to track tasks and bugs. | |
29 | -3. **Open Pull Requests**: Push a branch and open a PR to review code. | |
30 | 8 | |
9 | +Navigate to your instance URL (e.g., `http://localhost:4321`) and log in with the admin credentials you created during installation. | |
31 | 10 | |
32 | -## Next Steps | |
11 | +## 2. Create a Repository | |
12 | + | |
13 | +1. Click the **+** icon in the top navigation bar | |
14 | +2. Select **New Repository** | |
15 | +3. Enter a name (e.g., `my-project`) | |
16 | +4. Choose visibility: **Public** or **Private** | |
17 | +5. Optionally initialize with a README | |
18 | +6. Click **Create Repository** | |
19 | + | |
20 | +## 3. Push Code | |
21 | + | |
22 | +```bash | |
23 | +# Clone your new repository | |
24 | +git clone https://git.yourcompany.com/your-username/my-project.git | |
25 | +cd my-project | |
26 | + | |
27 | +# Add your code | |
28 | +echo "# My Project" > README.md | |
29 | +git add . | |
30 | +git commit -m "Initial commit" | |
31 | +git push -u origin main | |
32 | +``` | |
33 | + | |
34 | +Or use SSH: | |
35 | + | |
36 | +```bash | |
37 | +git clone ssh://git@your-server:2222/your-username/my-project.git | |
38 | +``` | |
39 | + | |
40 | +## 4. Explore the Dashboard | |
41 | + | |
42 | +Your dashboard shows: | |
43 | +- **Recent Repositories** you've worked on | |
44 | +- **Pull Requests** requiring your attention | |
45 | +- **Stacked PRs** if you're using the stack workflow | |
46 | +- **Activity Feed** from your team | |
47 | + | |
48 | +## 5. Create a Pull Request | |
49 | + | |
50 | +1. Navigate to your repository | |
51 | +2. Click **Pull Requests** → **New Pull Request** | |
52 | +3. Select source and target branches | |
53 | +4. Add a title and description | |
54 | +5. Click **Create Pull Request** | |
55 | + | |
56 | +See [First Repository](/getting-started/first-repository/) for a complete walkthrough. | |
33 | 57 | |
34 | -Now that you're exploring the UI, try pushing your first code: | |
58 | +## 6. Try Key Features | |
59 | + | |
60 | +### Stacked PRs | |
61 | +Break large changes into reviewable stacks: | |
62 | +```bash | |
63 | +# Install the CLI | |
64 | +npm install -g opencodehub-cli | |
65 | + | |
66 | +# Login | |
67 | +och auth login | |
68 | + | |
69 | +# Create a stacked branch | |
70 | +och stack create feature/part-1 | |
71 | + | |
72 | +# Make changes, commit, push | |
73 | +och stack submit | |
74 | +``` | |
75 | + | |
76 | +### AI Code Review | |
77 | +Enable in **Settings** → **AI Review**. Configure your preferred provider (OpenAI, Anthropic, etc.) and AI will automatically review PRs for bugs and security issues. | |
78 | + | |
79 | +### Merge Queue | |
80 | +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. | |
81 | + | |
82 | +### CI/CD Pipelines | |
83 | +Create `.github/workflows/*.yml` files in your repository. OpenCodeHub uses GitHub Actions-compatible syntax. | |
84 | + | |
85 | +## 7. Install the CLI | |
86 | + | |
87 | +```bash | |
88 | +npm install -g opencodehub-cli | |
89 | + | |
90 | +# Authenticate | |
91 | +och auth login --url https://git.yourcompany.com | |
92 | + | |
93 | +# Check status | |
94 | +och status | |
95 | +``` | |
96 | + | |
97 | +See [CLI Overview](/reference/cli-overview/) for all commands. | |
98 | + | |
99 | +## Next Steps | |
35 | 100 | |
36 | -👉 **[Create Your First Repository](first-repository.md)** | |
101 | +- **[First Repository](/getting-started/first-repository/)** — Complete repository lifecycle | |
102 | +- **[Stacked PRs](/features/stacked-prs/)** — Learn the stack workflow | |
103 | +- **[CLI Reference](/reference/cli-commands/)** — All CLI commands | |
104 | +- **[Configuration](/administration/configuration/)** — Customize your instance | |
37 | 105 | |
▼docs-site/src/content/docs/guides/example.md
+0-9
| @@ -1,11 +0,0 @@ | ||
1 | -title: Example Guide | |
2 | -description: A guide in my new Starlight docs site. | |
3 | - | |
4 | -Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. | |
5 | -Writing a good guide requires thinking about what your users are trying to do. | |
6 | - | |
7 | -## Further reading | |
8 | - | |
9 | -- Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework | |
10 | 0 | |
▼docs-site/src/content/docs/index.mdx
+103-70
| @@ -1,9 +1,9 @@ | ||
1 | 1 | --- |
2 | 2 | title: OpenCodeHub Documentation |
3 | -description: The open-source, AI-native Git server with Stacked PRs and Smart Merge Queues. | |
3 | +description: The open-source, self-hosted Git platform with Stacked PRs, AI Code Review, and Smart Merge Queues. | |
4 | 4 | hero: |
5 | 5 | title: OpenCodeHub |
6 | - tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, and a modern developer experience. | |
6 | + tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, AI code review, and a modern developer experience. | |
7 | 7 | --- |
8 | 8 | |
9 | 9 | import { Card, CardGrid } from "@astrojs/starlight/components"; |
| @@ -11,110 +11,143 @@ import { Card, CardGrid } from "@astrojs/starlight/components"; | ||
11 | 11 | ## Why OpenCodeHub? |
12 | 12 | |
13 | 13 | <CardGrid stagger> |
14 | - <Card title="🚀 Stacked PRs" icon="rocket"> | |
15 | - Break large features into small, dependent steps. Ship faster without the | |
16 | - review bottleneck. | |
14 | + <Card title="Stacked PRs" icon="rocket"> | |
15 | + Break large features into small, dependent steps. Graphite-style stacked branches with automatic rebase and merge readiness checks. | |
17 | 16 | </Card> |
18 | - <Card title="🤖 AI Code Review" icon="star"> | |
19 | - Built-in LLM agents (GPT-4/Claude) review your code for bugs and security | |
20 | - issues automatically. | |
17 | + <Card title="AI Code Review" icon="star"> | |
18 | + Built-in LLM agents (GPT-4, Claude, Gemini, Groq, Ollama, and 10+ providers) review your code for bugs and security issues automatically. | |
21 | 19 | </Card> |
22 | - <Card title="🔀 Smart Merge Queue" icon="random"> | |
23 | - Automate your merging strategy. Ensure `main` never breaks with speculative | |
24 | - CI runs. | |
20 | + <Card title="Smart Merge Queue" icon="random"> | |
21 | + Stack-aware merge queue with speculative CI builds, priority lanes, and conflict detection. Ensure `main` never breaks. | |
25 | 22 | </Card> |
26 | - <Card title="🏠 Fully Self-Hosted" icon="laptop"> | |
27 | - Keep your code on your servers. No "per-seat" pricing. No vendor lock-in. | |
23 | + <Card title="CI/CD Pipelines" icon="add-document"> | |
24 | + GitHub Actions-compatible workflow engine with Docker-based runners, artifact storage, and secrets management. | |
25 | + </Card> | |
26 | + <Card title="Fully Self-Hosted" icon="laptop"> | |
27 | + Keep your code on your servers. No per-seat pricing. No vendor lock-in. Deploy with Docker, Podman, or bare metal. | |
28 | + </Card> | |
29 | + <Card title="Package Registry" icon="seti:db"> | |
30 | + Built-in npm and Docker container registry. Host private packages alongside your source code. | |
28 | 31 | </Card> |
29 | 32 | </CardGrid> |
30 | 33 | |
31 | -## Architecture Overview | |
32 | - | |
33 | - | |
34 | +## Core Capabilities | |
34 | 35 | |
35 | -OpenCodeHub is built on a modern, scalable architecture designed for performance and flexibility. | |
36 | +<CardGrid> | |
37 | + <Card title="Git Hosting" icon="seti:git"> | |
38 | + Full Git support with HTTP smart protocol and SSH. Repository forking, mirroring, LFS, and CODEOWNERS enforcement. | |
39 | + </Card> | |
40 | + <Card title="Pull Requests" icon="seti:code"> | |
41 | + Full PR lifecycle with inline comments, file-level approvals, review templates, reviewer routing rules, and auto-merge. | |
42 | + </Card> | |
43 | + <Card title="Issues & Projects" icon="seti:folder"> | |
44 | + Issue tracking with custom fields and statuses. Drag-and-drop project boards with columns and cards. | |
45 | + </Card> | |
46 | + <Card title="Security" icon="seti:lock"> | |
47 | + Secret scanning, vulnerability scanning, security policies, path-level permissions, and audit logging. | |
48 | + </Card> | |
49 | + <Card title="Webhooks & Automations" icon="seti:webhook"> | |
50 | + Outbound webhooks with HMAC signing. Rule-based automations for PR workflows, reviews, and deployments. | |
51 | + </Card> | |
52 | + <Card title="Organizations & Teams" icon="seti:user"> | |
53 | + Multi-tenant organizations with role-based access control, team management, and fine-grained permissions. | |
54 | + </Card> | |
55 | +</CardGrid> | |
36 | 56 | |
37 | 57 | ## Deploy Anywhere |
38 | 58 | |
39 | 59 | <CardGrid> |
40 | - <Card title="🐳 Docker" icon="add-document"> | |
41 | - The recommended way to deploy. Full Docker Compose setup with PostgreSQL and | |
42 | - Redis. [Deploy with Docker →](/administration/deploy-docker/) | |
60 | + <Card title="Docker" icon="add-document"> | |
61 | + The recommended way to deploy. Full Docker Compose setup with PostgreSQL and Redis. | |
62 | + [Deploy with Docker →](/administration/deploy-docker/) | |
43 | 63 | </Card> |
44 | - <Card title="🚀 Coolify" icon="rocket"> | |
45 | - Self-hosted PaaS alternative to Heroku/Render. Auto-deploy with built-in SSL | |
46 | - and managed databases. [Deploy on Coolify →](/administration/deploy-coolify/) | |
64 | + <Card title="Podman" icon="add-document"> | |
65 | + Rootless containers with native systemd integration. Ideal for security-focused deployments. | |
66 | + [Deploy with Podman →](/administration/deploy-podman/) | |
47 | 67 | </Card> |
48 | - <Card title="🖥️ CyberPanel" icon="laptop"> | |
49 | - Modern hosting panel with OpenLiteSpeed. Docker or native Node.js deployment. | |
50 | - [Deploy on CyberPanel →](/administration/deploy-cyberpanel/) | |
68 | + <Card title="Kubernetes" icon="add-document"> | |
69 | + Deploy to Kubernetes using the bundled Helm chart with horizontal scaling. | |
70 | + [Deploy on Kubernetes →](/administration/kubernetes/) | |
51 | 71 | </Card> |
52 | - <Card title="📁 cPanel" icon="seti:folder"> | |
53 | - Deploy on shared hosting using Node.js or PM2. Best for budget hosting. | |
54 | - [Deploy on cPanel →](/administration/deploy-cpanel/) | |
72 | + <Card title="Coolify" icon="rocket"> | |
73 | + Self-hosted PaaS alternative to Heroku/Render. Auto-deploy with built-in SSL. | |
74 | + [Deploy on Coolify →](/administration/deploy-coolify/) | |
55 | 75 | </Card> |
56 | - <Card title="💾 NAS" icon="seti:db"> | |
76 | + <Card title="NAS" icon="seti:db"> | |
57 | 77 | Run on Synology, TrueNAS, or QNAP. Perfect for private home/team Git servers. |
58 | 78 | [Deploy on NAS →](/administration/deploy-nas/) |
59 | 79 | </Card> |
60 | - <Card title="🦭 Podman" icon="add-document"> | |
61 | - Rootless containers with native systemd integration. Ideal for | |
62 | - security-focused deployments. [Deploy with Podman | |
63 | - →](/administration/deploy-podman/) | |
64 | - </Card> | |
65 | - <Card title="☁️ Cloudflare" icon="add-document"> | |
66 | - Use Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage. | |
80 | + <Card title="Cloudflare" icon="add-document"> | |
81 | + Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage. | |
67 | 82 | [Deploy with Cloudflare →](/administration/deploy-cloudflare/) |
68 | 83 | </Card> |
69 | - <Card title="🌐 Nginx" icon="add-document"> | |
70 | - Production-ready reverse proxy config with SSL and Git-specific | |
71 | - optimizations. [Configure Nginx →](/administration/deploy-nginx/) | |
84 | + <Card title="Nginx" icon="add-document"> | |
85 | + Production-ready reverse proxy config with SSL and Git-specific optimizations. | |
86 | + [Configure Nginx →](/administration/deploy-nginx/) | |
87 | + </Card> | |
88 | + <Card title="cPanel / CyberPanel" icon="seti:folder"> | |
89 | + Deploy on shared hosting or VPS panels using Node.js + PM2. | |
90 | + [cPanel →](/administration/deploy-cpanel/) | [CyberPanel →](/administration/deploy-cyberpanel/) | |
72 | 91 | </Card> |
73 | 92 | </CardGrid> |
74 | 93 | |
75 | 94 | ## Tech Stack |
76 | 95 | |
77 | -| Layer | Technology | | |
78 | -| ------------ | ------------------------------------- | | |
79 | -| **Frontend** | Astro, React, Tailwind CSS, shadcn/ui | | |
80 | -| **Backend** | Node.js, Astro API Routes, simple-git | | |
81 | -| **Database** | PostgreSQL, Turso (LibSQL), SQLite | | |
82 | -| **Storage** | Local disk, S3, R2 | | |
83 | -| **Auth** | JWT, 2FA, OAuth (GitHub, Google) | | |
84 | -| **CI/CD** | GitHub Actions compatible runners | | |
96 | +| Layer | Technology | | |
97 | +| --- | --- | | |
98 | +| **Framework** | Astro 4.x (SSR, Node standalone adapter) | | |
99 | +| **Frontend** | React 18, Tailwind CSS, Radix UI, Framer Motion | | |
100 | +| **Backend** | Astro API Routes (175+ endpoints), GraphQL | | |
101 | +| **Database** | PostgreSQL (prod), SQLite (dev), Turso/LibSQL (edge) | | |
102 | +| **ORM** | Drizzle ORM (38 schema tables) | | |
103 | +| **Auth** | JWT, bcryptjs, TOTP 2FA, OAuth (Google, GitHub), SSO/SAML, OIDC | | |
104 | +| **Git** | simple-git, isomorphic-git, nodegit, ssh2 | | |
105 | +| **Storage** | Local filesystem or S3-compatible (AWS, MinIO, R2, Garage, Wasabi) | | |
106 | +| **CI/CD** | GitHub Actions-compatible engine, Docker-based runners | | |
107 | +| **AI** | OpenAI, Anthropic, Google, Groq, Ollama, OpenRouter, Together, Bytez | | |
108 | +| **Queue** | BullMQ + Redis | | |
109 | +| **Observability** | OpenTelemetry, Prometheus metrics, Pino logging | | |
110 | + | |
111 | +## Quick Start | |
112 | + | |
113 | +```bash | |
114 | +# Clone and start with Docker Compose | |
115 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
116 | +cd OpencodeHub | |
117 | +cp .env.example .env # Edit secrets! | |
118 | +docker-compose up -d | |
119 | + | |
120 | +# Create admin user | |
121 | +docker-compose exec app bun run scripts/seed-admin.ts | |
122 | + | |
123 | +# Open in browser | |
124 | +open http://localhost:4321 | |
125 | +``` | |
126 | + | |
127 | +:::tip[SSH Git Access] | |
128 | +The SSH git server starts automatically on port 2222. Clone with: | |
129 | +`git clone ssh://git@localhost:2222/owner/repo.git` | |
130 | +::: | |
85 | 131 | |
86 | 132 | ## Quick Links |
87 | 133 | |
88 | 134 | <CardGrid> |
89 | 135 | <Card title="Installation" icon="add-document"> |
90 | - Get OpenCodeHub running in minutes using our 1-click install script. [Read the guide | |
91 | - →](/getting-started/installation/) | |
136 | + Get OpenCodeHub running in minutes. [Read the guide →](/getting-started/installation/) | |
92 | 137 | </Card> |
93 | - <Card title="CircleCI Integration" icon="seti:check"> | |
94 | - Connect CircleCI as your external CI provider. Trigger builds and report | |
95 | - PR checks automatically. [Set up CircleCI →](/guides/circleci-integration/) | |
138 | + <Card title="Configuration" icon="seti:config"> | |
139 | + All environment variables and settings. [View config →](/administration/configuration/) | |
96 | 140 | </Card> |
97 | 141 | <Card title="CLI Reference" icon="seti:shell"> |
98 | - Learn the `och` CLI commands for managing stacks and repositories. [View | |
99 | - commands →](/reference/cli-commands/) | |
142 | + The `och` CLI with 20+ command groups. [View commands →](/reference/cli-commands/) | |
100 | 143 | </Card> |
101 | 144 | <Card title="API Reference" icon="open-book"> |
102 | - Integrate with OpenCodeHub using the REST API. [Explore the API | |
103 | - →](/api/rest-api/) | |
145 | + REST API with 175+ endpoints. [Explore the API →](/api/rest-api/) | |
146 | + </Card> | |
147 | + <Card title="Architecture" icon="seti:code"> | |
148 | + System design, data flow, and component overview. [View architecture →](/development/architecture/) | |
104 | 149 | </Card> |
105 | 150 | <Card title="Contributing" icon="github"> |
106 | - Help make OpenCodeHub better. Set up your local dev environment. [Start | |
107 | - contributing →](/development/contributing/) | |
151 | + Help make OpenCodeHub better. [Start contributing →](/development/contributing/) | |
108 | 152 | </Card> |
109 | 153 | </CardGrid> |
110 | - | |
111 | -## Storage Options | |
112 | - | |
113 | -:::tip[Performance Recommendation] | |
114 | -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. | |
115 | -::: | |
116 | - | |
117 | -| Storage Type | Performance | Cost | Best For | | |
118 | -| ---------------- | ----------- | ---- | ----------------------- | | |
119 | -| **Local SSD** | ⚡ Fastest | $$ | Production, self-hosted | | |
120 | -| **S3/R2** | 🐢 Slow | $ | Backup, multi-node | | |
121 | 154 | |
▼docs-site/src/content/docs/reference/api-versioning.md
+14-0
| @@ -0,0 +1,14 @@ | ||
1 | +# API Versioning | |
2 | + | |
3 | +OpenCodeHub provides a stable REST API and GraphQL endpoint. | |
4 | + | |
5 | +## Versioning Strategy | |
6 | +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. | |
7 | + | |
8 | +When breaking changes are unavoidable: | |
9 | +- They will be announced in the release notes. | |
10 | +- A deprecation header (`Deprecation: <date>`) will be added to the response of the old endpoint. | |
11 | +- Clients will have at least one minor release cycle to migrate. | |
12 | + | |
13 | +## GraphQL | |
14 | +The GraphQL API inherently supports seamless evolution by deprecating specific fields using the `@deprecated` directive, allowing clients to transition without breaking version changes. | |
0 | 15 | |
▼docs-site/src/content/docs/reference/cli-auth-config.md
+2-2
| @@ -10,12 +10,12 @@ This page covers how authentication and local configuration work in the OpenCode | ||
10 | 10 | ### Login |
11 | 11 | |
12 | 12 | ```bash |
13 | -och auth login --url http://localhost:3000 | |
13 | +och auth login --url http://localhost:4321 | |
14 | 14 | ``` |
15 | 15 | |
16 | 16 | Options: |
17 | 17 | |
18 | -- `--url <url>`: Instance URL (defaults to `http://localhost:3000`) | |
18 | +- `--url <url>`: Instance URL (defaults to `http://localhost:4321`) | |
19 | 19 | - `--with-token`: Prompt for a personal access token |
20 | 20 | - `--token <token>`: Non‑interactive token login |
21 | 21 | |
22 | 22 | |
▼docs-site/src/content/docs/reference/cli-core-commands.md
+1-1
| @@ -16,7 +16,7 @@ och repo list | ||
16 | 16 | Shorthand: |
17 | 17 | |
18 | 18 | ```bash |
19 | -och init --url http://localhost:3000 | |
19 | +och init --url http://localhost:4321 | |
20 | 20 | och push --branch main |
21 | 21 | ``` |
22 | 22 | |
23 | 23 | |
▼docs-site/src/content/docs/reference/example.md
+0-9
| @@ -1,11 +0,0 @@ | ||
1 | -title: Example Reference | |
2 | -description: A reference page in my new Starlight docs site. | |
3 | - | |
4 | -Reference pages are ideal for outlining how things work in terse and clear terms. | |
5 | -Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what you're documenting. | |
6 | - | |
7 | -## Further reading | |
8 | - | |
9 | -- Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework | |
10 | 0 | |
▼docs-site/src/content/docs/tutorials/zero-to-production.md
+1-1
| @@ -34,7 +34,7 @@ bun run scripts/seed-admin.ts | ||
34 | 34 | npm run dev |
35 | 35 | ``` |
36 | 36 | |
37 | -Open the app at `http://localhost:3000` and log in. | |
37 | +Open the app at `http://localhost:4321` and log in. | |
38 | 38 | |
39 | 39 | ## 5) Production deployment |
40 | 40 | |
41 | 41 | |
▼docs-site/src/custom.css
+4-4
| @@ -11,10 +11,10 @@ | ||
11 | 11 | } |
12 | 12 | |
13 | 13 | :root { |
14 | - --och-accent-1: #2dd4bf; | |
15 | - --och-accent-2: #8be9fd; | |
16 | - --och-accent-3: #bd93f9; | |
17 | - --och-accent-4: #ff79c6; | |
14 | + --och-accent-1: #0d9488; | |
15 | + --och-accent-2: #38bdf8; | |
16 | + --och-accent-3: #a78bfa; | |
17 | + --och-accent-4: #f472b6; | |
18 | 18 | |
19 | 19 | --och-hero-surface: color-mix(in srgb, var(--sl-color-bg) 92%, transparent); |
20 | 20 | } |
21 | 21 | |
▼docs/FREE-DEPLOYMENT.md
+395-0
| @@ -0,0 +1,395 @@ | ||
1 | +# OpenCodeHub — Free Deployment Options | |
2 | + | |
3 | +> Deploy OpenCodeHub for free with persistent file storage for git repositories. | |
4 | + | |
5 | +--- | |
6 | + | |
7 | +## Quick Comparison | |
8 | + | |
9 | +| Platform | Cost | Storage | RAM | CPU | Best For | | |
10 | +|----------|------|---------|-----|-----|----------| | |
11 | +| **Oracle Cloud Free** | $0 forever | 200GB | 24GB | 4 ARM cores | Best overall | | |
12 | +| **Home Server/NAS** | $0 | Unlimited | Your hardware | Your hardware | Privacy-focused | | |
13 | +| **Fly.io** | $0 (limited) | 3GB free | 256MB | Shared | Quick testing | | |
14 | +| **Google Cloud Free** | $0 forever | 30GB | 1GB | 0.25 vCPU | Light usage | | |
15 | +| **Railway** | $5 trial | 1GB | 512MB | Shared | Quick demo | | |
16 | +| **Hetzner** | ~€4/mo | 40GB | 4GB | 2 vCPU | Best value paid | | |
17 | + | |
18 | +--- | |
19 | + | |
20 | +## Option 1: Oracle Cloud Free Tier (Recommended) | |
21 | + | |
22 | +**Always Free** — No credit card charge, no time limit. | |
23 | + | |
24 | +### What You Get | |
25 | +- 4 ARM cores (Ampere A1) | |
26 | +- 24 GB RAM | |
27 | +- 200 GB boot volume | |
28 | +- 2 x 1TB block volumes (total 2TB) | |
29 | +- Free PostgreSQL database | |
30 | +- Free Redis | |
31 | + | |
32 | +### Steps | |
33 | + | |
34 | +```bash | |
35 | +# 1. Sign up at cloud.oracle.com | |
36 | +# 2. Create a VM instance: | |
37 | +# - Name: opencodehub | |
38 | +# - Image: Ubuntu 22.04 | |
39 | +# - Shape: VM.Standard.A1.Flex (4 OCPU, 24 GB RAM) | |
40 | +# - Upload SSH key | |
41 | +# - Create boot volume: 100 GB | |
42 | + | |
43 | +# 3. SSH into your instance | |
44 | +ssh -i ~/.ssh/key ubuntu@YOUR_PUBLIC_IP | |
45 | + | |
46 | +# 4. Install Docker | |
47 | +curl -fsSL https://get.docker.com -o get-docker.sh | |
48 | +sudo sh get-docker.sh | |
49 | +sudo usermod -aG docker $USER | |
50 | + | |
51 | +# 5. Install Docker Compose | |
52 | +sudo apt install docker-compose-plugin -y | |
53 | + | |
54 | +# 6. Clone OpenCodeHub | |
55 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
56 | +cd OpencodeHub | |
57 | + | |
58 | +# 7. Configure | |
59 | +cp .env.example .env | |
60 | +nano .env # Edit secrets | |
61 | + | |
62 | +# 8. Start | |
63 | +sudo docker compose up -d | |
64 | + | |
65 | +# 9. Create admin | |
66 | +sudo docker compose exec app bun run scripts/seed-admin.ts | |
67 | +``` | |
68 | + | |
69 | +### Access Your Instance | |
70 | +``` | |
71 | +Web: http://YOUR_PUBLIC_IP:4321 | |
72 | +SSH: ssh://git@YOUR_PUBLIC_IP:2222/owner/repo.git | |
73 | +HTTPS: Set up Nginx + Let's Encrypt (free) | |
74 | +``` | |
75 | + | |
76 | +--- | |
77 | + | |
78 | +## Option 2: Home Server / NAS | |
79 | + | |
80 | +**$0 forever** — Use hardware you already own. | |
81 | + | |
82 | +### What You Need | |
83 | +- Any computer running Linux/Mac/Windows | |
84 | +- 2GB+ RAM recommended | |
85 | +- 20GB+ free disk space | |
86 | + | |
87 | +### Quick Start (Linux/Mac) | |
88 | + | |
89 | +```bash | |
90 | +# Install Docker | |
91 | +curl -fsSL https://get.docker.com | sh | |
92 | + | |
93 | +# Clone and start | |
94 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
95 | +cd OpencodeHub | |
96 | +cp .env.example .env | |
97 | +nano .env # Set secrets | |
98 | +docker compose up -d | |
99 | + | |
100 | +# Create admin | |
101 | +docker compose exec app bun run scripts/seed-admin.ts | |
102 | +``` | |
103 | + | |
104 | +### NAS Deployment | |
105 | + | |
106 | +**Synology / TrueNAS / QNAP:** | |
107 | +1. Install Docker from Package Center | |
108 | +2. Use Docker Compose or Portainer | |
109 | +3. Mount a shared folder for `/data` | |
110 | + | |
111 | +### Access from Anywhere | |
112 | +- **Tailscale** (free): `tailscale up` → access via Tailscale IP | |
113 | +- **Cloudflare Tunnel** (free): Expose without opening ports | |
114 | +- **Port forwarding**: Open port 4321 on your router | |
115 | + | |
116 | +--- | |
117 | + | |
118 | +## Option 3: Fly.io Free Tier | |
119 | + | |
120 | +**Free** — 3GB persistent volume included. | |
121 | + | |
122 | +### Steps | |
123 | + | |
124 | +```bash | |
125 | +# 1. Install flyctl | |
126 | +curl -L https://fly.io/install.sh | sh | |
127 | + | |
128 | +# 2. Login | |
129 | +fly auth login | |
130 | + | |
131 | +# 3. Launch | |
132 | +fly launch --name opencodehub | |
133 | + | |
134 | +# 4. Add persistent volume | |
135 | +fly volume create opencodehub_data --region sjc --size 3 | |
136 | + | |
137 | +# 5. Set secrets | |
138 | +fly secrets set JWT_SECRET=$(openssl rand -hex 32) | |
139 | +fly secrets set SESSION_SECRET=$(openssl rand -hex 32) | |
140 | +fly secrets set DATABASE_URL="postgresql://..." | |
141 | +fly secrets set REDIS_URL="redis://..." | |
142 | + | |
143 | +# 6. Deploy | |
144 | +fly deploy | |
145 | +``` | |
146 | + | |
147 | +### Limitations | |
148 | +- 3GB storage (enough for small repos) | |
149 | +- 256MB RAM (may be slow for large repos) | |
150 | +- Apps休眠 after inactivity (cold starts take ~30s) | |
151 | + | |
152 | +--- | |
153 | + | |
154 | +## Option 4: Google Cloud Free Tier | |
155 | + | |
156 | +**Always Free** — e2-micro instance. | |
157 | + | |
158 | +### Steps | |
159 | + | |
160 | +```bash | |
161 | +# 1. Sign up at cloud.google.com | |
162 | +# 2. Create e2-micro VM: | |
163 | +# - Machine type: e2-micro (0.25 vCPU, 1GB RAM) | |
164 | +# - Boot disk: 30GB SSD (free) | |
165 | +# - Firewall: Allow HTTP (80), SSH (22) | |
166 | + | |
167 | +# 3. SSH in and install Docker | |
168 | +ssh -i ~/.ssh/key USER@EXTERNAL_IP | |
169 | +sudo apt update && sudo apt install docker.io docker-compose-plugin -y | |
170 | + | |
171 | +# 4. Deploy OpenCodeHub | |
172 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
173 | +cd OpencodeHub | |
174 | +cp .env.example .env | |
175 | +nano .env | |
176 | +docker compose up -d | |
177 | +``` | |
178 | + | |
179 | +### Limitations | |
180 | +- Only 1GB RAM (tight for production) | |
181 | +- 0.25 vCPU (slow builds) | |
182 | +- Good for personal/light use | |
183 | + | |
184 | +--- | |
185 | + | |
186 | +## Option 5: Self-Hosted on Existing VPS | |
187 | + | |
188 | +If you already have a VPS (Hetzner, DigitalOcean, Linode, etc.): | |
189 | + | |
190 | +```bash | |
191 | +# SSH into your server | |
192 | +ssh root@YOUR_SERVER_IP | |
193 | + | |
194 | +# One-line install | |
195 | +curl -fsSL https://raw.githubusercontent.com/swadhinbiswas/OpencodeHub/main/install.sh | bash | |
196 | +``` | |
197 | + | |
198 | +--- | |
199 | + | |
200 | +## Option 6: Docker on Windows/Mac | |
201 | + | |
202 | +For local development or personal use: | |
203 | + | |
204 | +```bash | |
205 | +# Install Docker Desktop for Windows/Mac | |
206 | +# https://docs.docker.com/get-docker/ | |
207 | + | |
208 | +# Clone and start | |
209 | +git clone https://github.com/swadhinbiswas/OpencodeHub.git | |
210 | +cd OpencodeHub | |
211 | +cp .env.example .env | |
212 | +nano .env | |
213 | +docker compose up -d | |
214 | + | |
215 | +# Create admin | |
216 | +docker compose exec app bun run scripts/seed-admin.ts | |
217 | +``` | |
218 | + | |
219 | +--- | |
220 | + | |
221 | +## Storage Configuration | |
222 | + | |
223 | +### Local Storage (Default) | |
224 | +```bash | |
225 | +STORAGE_TYPE=local | |
226 | +STORAGE_PATH=/data/storage | |
227 | +GIT_REPOS_PATH=/data/repositories | |
228 | +``` | |
229 | + | |
230 | +### S3-Compatible (MinIO on same server) | |
231 | +```bash | |
232 | +# Add to docker-compose.yml: | |
233 | +minio: | |
234 | + image: minio/minio | |
235 | + command: server /data --console-address ":9001" | |
236 | + volumes: | |
237 | + - ./data/minio:/data | |
238 | + ports: | |
239 | + - "9001:9001" | |
240 | + | |
241 | +STORAGE_TYPE=s3 | |
242 | +STORAGE_ENDPOINT=http://minio:9000 | |
243 | +STORAGE_BUCKET=opencodehub | |
244 | +STORAGE_ACCESS_KEY_ID=minioadmin | |
245 | +STORAGE_SECRET_ACCESS_KEY=minioadmin | |
246 | +``` | |
247 | + | |
248 | +--- | |
249 | + | |
250 | +## HTTPS Setup (Free) | |
251 | + | |
252 | +### With Let's Encrypt + Nginx | |
253 | + | |
254 | +```bash | |
255 | +# Install Nginx and Certbot | |
256 | +sudo apt install nginx certbot python3-certbot-nginx -y | |
257 | + | |
258 | +# Create Nginx config | |
259 | +sudo nano /etc/nginx/sites-available/opencodehub | |
260 | +``` | |
261 | + | |
262 | +```nginx | |
263 | +server { | |
264 | + listen 80; | |
265 | + server_name git.yourdomain.com; | |
266 | + return 301 https://$host$request_uri; | |
267 | +} | |
268 | + | |
269 | +server { | |
270 | + listen 443 ssl http2; | |
271 | + server_name git.yourdomain.com; | |
272 | + | |
273 | + ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; | |
274 | + ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; | |
275 | + | |
276 | + client_max_body_size 500M; | |
277 | + | |
278 | + location / { | |
279 | + proxy_pass http://localhost:4321; | |
280 | + proxy_set_header Host $host; | |
281 | + proxy_set_header X-Real-IP $remote_addr; | |
282 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
283 | + proxy_set_header X-Forwarded-Proto $scheme; | |
284 | + } | |
285 | + | |
286 | + # WebSocket support | |
287 | + location /api/realtime { | |
288 | + proxy_pass http://localhost:4321; | |
289 | + proxy_http_version 1.1; | |
290 | + proxy_set_header Upgrade $http_upgrade; | |
291 | + proxy_set_header Connection "upgrade"; | |
292 | + } | |
293 | +} | |
294 | +``` | |
295 | + | |
296 | +```bash | |
297 | +# Enable and get SSL | |
298 | +sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/ | |
299 | +sudo certbot --nginx -d git.yourdomain.com | |
300 | +``` | |
301 | + | |
302 | +--- | |
303 | + | |
304 | +## Environment Variables (Production) | |
305 | + | |
306 | +```bash | |
307 | +# Generate all secrets | |
308 | +JWT_SECRET=$(openssl rand -hex 32) | |
309 | +SESSION_SECRET=$(openssl rand -hex 32) | |
310 | +INTERNAL_HOOK_SECRET=$(openssl rand -hex 32) | |
311 | +CRON_SECRET=$(openssl rand -hex 32) | |
312 | +RUNNER_SECRET=$(openssl rand -hex 32) | |
313 | +AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) | |
314 | +WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) | |
315 | + | |
316 | +# Your .env file | |
317 | +DATABASE_DRIVER=postgres | |
318 | +DATABASE_URL=postgresql://opencodehub:PASSWORD@localhost:5432/opencodehub | |
319 | +DATABASE_SSL=false | |
320 | +REDIS_URL=redis://localhost:6379 | |
321 | +SITE_URL=https://git.yourdomain.com | |
322 | +STORAGE_TYPE=local | |
323 | +GIT_REPOS_PATH=/data/repositories | |
324 | +NODE_ENV=production | |
325 | +``` | |
326 | + | |
327 | +--- | |
328 | + | |
329 | +## Backup Strategy | |
330 | + | |
331 | +### Automated Backup Script | |
332 | +```bash | |
333 | +#!/bin/bash | |
334 | +# backup.sh — Run daily via cron | |
335 | +BACKUP_DIR="/backups/$(date +%Y-%m-%d)" | |
336 | +mkdir -p "$BACKUP_DIR" | |
337 | + | |
338 | +# Database dump | |
339 | +docker compose exec -T postgres pg_dump -U opencodehub opencodehub > "$BACKUP_DIR/db.sql" | |
340 | + | |
341 | +# Git repos | |
342 | +tar czf "$BACKUP_DIR/repos.tar.gz" ./data/repositories/ | |
343 | + | |
344 | +# Upload to S3 (optional) | |
345 | +# aws s3 sync "$BACKUP_DIR" s3://your-backup-bucket/opencodehub/ | |
346 | +``` | |
347 | + | |
348 | +### Cron Job | |
349 | +```bash | |
350 | +# Add to crontab | |
351 | +crontab -e | |
352 | +0 2 * * * /path/to/backup.sh | |
353 | +``` | |
354 | + | |
355 | +--- | |
356 | + | |
357 | +## Monitoring | |
358 | + | |
359 | +### Health Check | |
360 | +```bash | |
361 | +# Check if app is running | |
362 | +curl -f http://localhost:4321/api/health || echo "DOWN" | |
363 | + | |
364 | +# Check Docker status | |
365 | +docker compose ps | |
366 | +docker compose logs --tail=50 app | |
367 | +``` | |
368 | + | |
369 | +### Uptime Robot (Free) | |
370 | +1. Sign up at uptimerobot.com | |
371 | +2. Add HTTP monitor: `http://YOUR_IP:4321/api/health` | |
372 | +3. Get notified if your instance goes down | |
373 | + | |
374 | +--- | |
375 | + | |
376 | +## Recommended Free Setup | |
377 | + | |
378 | +For a **personal Git server** that stores all your code: | |
379 | + | |
380 | +| Component | Choice | | |
381 | +|-----------|--------| | |
382 | +| **Cloud** | Oracle Cloud Free Tier | | |
383 | +| **Server** | ARM A1 (4 cores, 24GB RAM) | | |
384 | +| **Storage** | 100GB boot + 1TB block volume | | |
385 | +| **Database** | PostgreSQL (Docker) | | |
386 | +| **Cache** | Redis (Docker) | | |
387 | +| **HTTPS** | Nginx + Let's Encrypt | | |
388 | +| **Access** | Tailscale (private) or port forwarding | | |
389 | +| **Backups** | Cron script + local S3 | | |
390 | + | |
391 | +**Total cost: $0/month forever.** | |
392 | + | |
393 | +--- | |
394 | + | |
395 | +*Generated for OpenCodeHub free deployment.* | |
0 | 396 | |
▼docs/RENDER-DEPLOYMENT.md
+163-0
| @@ -0,0 +1,163 @@ | ||
1 | +# OpenCodeHub — Render Deployment Guide | |
2 | + | |
3 | +> Deploy to Render (Singapore) with free PostgreSQL + Upstash Redis. $0/month. | |
4 | + | |
5 | +--- | |
6 | + | |
7 | +## What You Get | |
8 | + | |
9 | +- OpenCodeHub running in Asia (Singapore) | |
10 | +- Free PostgreSQL database | |
11 | +- Free Redis via Upstash | |
12 | +- 1GB persistent storage for git repos | |
13 | +- HTTPS included | |
14 | +- Admin account | |
15 | + | |
16 | +--- | |
17 | + | |
18 | +## Step 1: Create Upstash Redis (2 min) | |
19 | + | |
20 | +1. Go to **https://upstash.com** → Sign up (free) | |
21 | +2. Click **Create Database** | |
22 | +3. Settings: | |
23 | + - **Name**: `opencodehub` | |
24 | + - **Region**: **Asia Pacific (Singapore)** | |
25 | + - **Plan**: Pay as you go (free tier: 10K commands/day) | |
26 | +4. Click **Create** | |
27 | +5. Copy the **Redis URL** (looks like `rediss://...@...`) | |
28 | +6. Keep this tab open — you'll need it in Step 3 | |
29 | + | |
30 | +--- | |
31 | + | |
32 | +## Step 2: Deploy to Render (5 min) | |
33 | + | |
34 | +1. Go to **https://render.com** → Sign up (free) | |
35 | +2. Click **New** → **Blueprint** | |
36 | +3. Connect your GitHub account | |
37 | +4. Select your OpenCodeHub repository | |
38 | +5. Render detects `render.yaml` and shows: | |
39 | + - `opencodehub-db` (PostgreSQL — Singapore) | |
40 | + - `opencodehub` (Web Service — Singapore) | |
41 | +6. Click **Apply** | |
42 | +7. Wait for deployment (~3-5 min first time) | |
43 | + | |
44 | +--- | |
45 | + | |
46 | +## Step 3: Set Redis URL (1 min) | |
47 | + | |
48 | +1. Go to your Web Service → **Environment** tab | |
49 | +2. Find `REDIS_URL` (shows "Not set") | |
50 | +3. Click the pencil icon → Paste your **Upstash Redis URL** | |
51 | +4. Click **Save** | |
52 | + | |
53 | +--- | |
54 | + | |
55 | +## Step 4: Set Site URL (1 min) | |
56 | + | |
57 | +1. In the same **Environment** tab | |
58 | +2. Find `SITE_URL` (shows "Not set") | |
59 | +3. Click the pencil icon → Enter your Render URL: | |
60 | + ``` | |
61 | + https://opencodehub.onrender.com | |
62 | + ``` | |
63 | + (Replace with your actual URL from the service overview) | |
64 | +4. Click **Save** | |
65 | + | |
66 | +--- | |
67 | + | |
68 | +## Step 5: Manual Deploy | |
69 | + | |
70 | +1. Go to your Web Service → **Manual Deploy** | |
71 | +2. Click **Deploy latest commit** | |
72 | +3. Wait for build to complete (~2-3 min) | |
73 | +4. Click the **URL** at the top to open your instance | |
74 | + | |
75 | +--- | |
76 | + | |
77 | +## Step 6: Create Admin Account | |
78 | + | |
79 | +You'll see the **Setup Wizard**. Fill in: | |
80 | + | |
81 | +| Field | Value | | |
82 | +|-------|-------| | |
83 | +| Username | `admin` (or your choice) | | |
84 | +| Email | Your email | | |
85 | +| Password | Strong password | | |
86 | + | |
87 | +Click **Create Account** — you're now logged in as admin! | |
88 | + | |
89 | +--- | |
90 | + | |
91 | +## Step 7: Test It | |
92 | + | |
93 | +```bash | |
94 | +# Create a repo via web UI, then clone: | |
95 | +git clone https://opencodehub.onrender.com/your-username/my-repo.git | |
96 | + | |
97 | +# Push code: | |
98 | +cd my-repo | |
99 | +echo "# Hello" > README.md | |
100 | +git add . && git commit -m "Initial" | |
101 | +git push origin main | |
102 | +``` | |
103 | + | |
104 | +--- | |
105 | + | |
106 | +## Done! Summary | |
107 | + | |
108 | +| Component | Service | Region | Cost | | |
109 | +|-----------|---------|--------|------| | |
110 | +| App | Render Free | Singapore | $0 | | |
111 | +| PostgreSQL | Render Free | Singapore | $0 | | |
112 | +| Redis | Upstash Free | Singapore | $0 | | |
113 | +| Storage | Render Disk 1GB | Singapore | $0 | | |
114 | +| **Total** | | | **$0/month** | | |
115 | + | |
116 | +--- | |
117 | + | |
118 | +## Keep Alive (Optional) | |
119 | + | |
120 | +Render free tier spins down after 15min inactivity. To prevent: | |
121 | + | |
122 | +1. Go to **https://uptimerobot.com** → Sign up (free) | |
123 | +2. Add Monitor → Type: HTTP(s) | |
124 | +3. URL: `https://opencodehub.onrender.com/api/health` | |
125 | +4. Monitoring interval: 5 minutes | |
126 | +5. Save — your app stays awake | |
127 | + | |
128 | +--- | |
129 | + | |
130 | +## Upgrade Path (When You Grow) | |
131 | + | |
132 | +| Need | Upgrade | Cost | | |
133 | +|------|---------|------| | |
134 | +| Always-on (no spin-down) | Render Starter | $7/mo | | |
135 | +| More storage (10GB+) | Render Disk upgrade | $1-7/mo | | |
136 | +| More Redis commands | Upstash Pay-as-you-go | ~$0.20/10K | | |
137 | +| PostgreSQL expiry | Render Starter DB | $7/mo | | |
138 | + | |
139 | +--- | |
140 | + | |
141 | +## Troubleshooting | |
142 | + | |
143 | +**App won't start?** | |
144 | +- Check **Logs** tab in Render | |
145 | +- Verify `REDIS_URL` is set correctly | |
146 | +- Ensure `SITE_URL` matches your Render URL | |
147 | + | |
148 | +**Redis connection failed?** | |
149 | +- Verify Redis URL from Upstash (starts with `rediss://`) | |
150 | +- Check Upstash dashboard shows "Active" | |
151 | +- Ensure region is Singapore | |
152 | + | |
153 | +**Git clone fails?** | |
154 | +- Use HTTPS: `git clone https://your-url.onrender.com/user/repo.git` | |
155 | +- SSH not available on Render free tier (use HTTPS) | |
156 | + | |
157 | +**Slow first request?** | |
158 | +- Normal for free tier (cold start ~30s) | |
159 | +- Add UptimeRobot ping to keep it warm | |
160 | + | |
161 | +--- | |
162 | + | |
163 | +*OpenCodeHub — Deployed on Render, Asia Region, $0/month.* | |
0 | 164 | |
▼docs/administration/air-gapped.md
+33-0
| @@ -0,0 +1,33 @@ | ||
1 | +# Air-gapped Environments | |
2 | + | |
3 | +OpenCodeHub can be deployed in fully offline, air-gapped networks. | |
4 | + | |
5 | +## Prerequisites | |
6 | +- A local container registry to host the OpenCodeHub Docker image. | |
7 | +- A local NPM/mirror registry if you plan to build from source. | |
8 | + | |
9 | +## Deployment Steps | |
10 | +1. On a machine with internet access, pull the required images: | |
11 | + ```bash | |
12 | + docker pull ghcr.io/swadhinbiswas/opencodehub:latest | |
13 | + docker pull postgres:15-alpine | |
14 | + docker pull redis:7-alpine | |
15 | + ``` | |
16 | +2. Save the images to tar archives: | |
17 | + ```bash | |
18 | + docker save ghcr.io/swadhinbiswas/opencodehub:latest -o och.tar | |
19 | + docker save postgres:15-alpine -o pg.tar | |
20 | + docker save redis:7-alpine -o redis.tar | |
21 | + ``` | |
22 | +3. Transfer the archives to the air-gapped network via secure media. | |
23 | +4. Load the images on the target machine: | |
24 | + ```bash | |
25 | + docker load -i och.tar | |
26 | + docker load -i pg.tar | |
27 | + docker load -i redis.tar | |
28 | + ``` | |
29 | + | |
30 | +## Configuration Adjustments | |
31 | +- Disable external OAuth providers (GitHub, Google) and rely exclusively on local authentication, LDAP, or an internal SAML provider. | |
32 | +- Disable outbound webhooks pointing to external SaaS platforms. | |
33 | +- 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. | |
0 | 34 | |
▼docs/administration/backup-restore.md
+29-0
| @@ -0,0 +1,29 @@ | ||
1 | +# Backup and Restore | |
2 | + | |
3 | +This guide covers how to back up and restore an OpenCodeHub instance. | |
4 | + | |
5 | +## Backup Strategy | |
6 | + | |
7 | +### 1. Database | |
8 | +For PostgreSQL, use `pg_dump`: | |
9 | +```bash | |
10 | +pg_dump -U opencodehub -d opencodehub_db > opencodehub_backup.sql | |
11 | +``` | |
12 | + | |
13 | +### 2. Git Repositories | |
14 | +The Git repositories are stored in the path defined by `GIT_REPOS_PATH` (default: `./data/repos`). | |
15 | +You can back this up using `rsync` or `tar`: | |
16 | +```bash | |
17 | +tar -czvf repos_backup.tar.gz ./data/repos/ | |
18 | +``` | |
19 | + | |
20 | +### 3. Object Storage | |
21 | +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. | |
22 | + | |
23 | +## Restore Strategy | |
24 | +To restore: | |
25 | +1. Stop the application and worker processes. | |
26 | +2. Restore the database using `psql`. | |
27 | +3. Extract the Git repositories to `GIT_REPOS_PATH`. | |
28 | +4. Restore object storage data. | |
29 | +5. Restart the application. | |
0 | 30 | |
▼docs/administration/configuration.md
+7-2
| @@ -6,8 +6,8 @@ OpenCodeHub is configured exclusively via environment variables. | ||
6 | 6 | |
7 | 7 | | Variable | Description | Required | Default | |
8 | 8 | |----------|-------------|----------|---------| |
9 | -| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:3000` | | |
10 | -| `PORT` | Port to listen on | No | `3000` | | |
9 | +| `SITE_URL` | Public URL of the instance | Yes | `http://localhost:4321` | | |
10 | +| `PORT` | Port to listen on | No | `4321` | | |
11 | 11 | | `NODE_ENV` | `development` or `production` | No | `development` | |
12 | 12 | |
13 | 13 | ## Security (Critical) |
| @@ -17,6 +17,11 @@ OpenCodeHub is configured exclusively via environment variables. | ||
17 | 17 | | `JWT_SECRET` | Signs auth tokens | `openssl rand -hex 32` | |
18 | 18 | | `SESSION_SECRET` | Signs session cookies | `openssl rand -hex 32` | |
19 | 19 | | `INTERNAL_HOOK_SECRET` | Secures git hooks | `openssl rand -hex 32` | |
20 | +| `CRON_SECRET` | Secures cron endpoints | `openssl rand -hex 32` | | |
21 | +| `RUNNER_SECRET` | Shared secret between server and CI runners | `openssl rand -hex 32` | | |
22 | +| `AI_CONFIG_ENCRYPTION_KEY` | Encrypts AI provider keys at rest | `openssl rand -hex 32` | | |
23 | +| `WORKFLOW_SECRET_ENCRYPTION_KEY` | Encrypts workflow/repo secrets at rest | `openssl rand -hex 32` | | |
24 | +| `METRICS_TOKEN` | Bearer token for `GET /api/metrics` (Prometheus). Empty = public | `openssl rand -hex 32` | | |
20 | 25 | |
21 | 26 | ## Database |
22 | 27 | |
23 | 28 | |
▼docs/administration/deploy-nas.md
+209-13
| @@ -4,6 +4,23 @@ | ||
4 | 4 | |
5 | 5 | 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. |
6 | 6 | |
7 | +> **Recommended:** the repository ships `docker-compose.nas.yml`, which is | |
8 | +> the production-safe version of every snippet below. It runs the web app, | |
9 | +> the **SSH Git server**, and the **background worker** as separate | |
10 | +> containers, requires secrets instead of defaulting them, and binds every | |
11 | +> data directory to one host path (`OCH_DATA_DIR`, default `/mnt/och`) — | |
12 | +> perfect for a dedicated hard disk or NAS share: | |
13 | +> | |
14 | +> ```bash | |
15 | +> cp .env.example .env # then fill in ALL secrets | |
16 | +> OCH_DATA_DIR=/volume1/opencodehub docker compose -f docker-compose.nas.yml up -d | |
17 | +> ``` | |
18 | +> | |
19 | +> The inline YAML in this guide is a minimal reference. Note that the **web | |
20 | +> app container does not run SSH Git** — if you use a snippet as-is, port | |
21 | +> 2222 will not serve SSH. Add the `ssh-git` service (or use | |
22 | +> `docker-compose.nas.yml`) for SSH access. | |
23 | + | |
7 | 24 | --- |
8 | 25 | |
9 | 26 | ## Prerequisites |
| @@ -135,7 +152,6 @@ docker run -d \ | ||
135 | 152 | --link opencodehub-postgres:postgres \ |
136 | 153 | --link opencodehub-redis:redis \ |
137 | 154 | -p 4321:4321 \ |
138 | - -p 2222:2222 \ | |
139 | 155 | -e NODE_ENV=production \ |
140 | 156 | -e DATABASE_URL=postgresql://opencodehub:[email protected]:5432/opencodehub \ |
141 | 157 | -e REDIS_URL=redis://:[email protected]:6379 \ |
| @@ -145,19 +161,24 @@ docker run -d \ | ||
145 | 161 | -e CRON_SECRET=$(openssl rand -hex 32) \ |
146 | 162 | -e RUNNER_SECRET=$(openssl rand -hex 32) \ |
147 | 163 | -e WORKFLOW_SECRET_ENCRYPTION_KEY=$(openssl rand -hex 32) \ |
164 | + -e AI_CONFIG_ENCRYPTION_KEY=$(openssl rand -hex 32) \ | |
148 | 165 | -e SITE_URL=https://git.yourddns.net \ |
149 | 166 | -e STORAGE_TYPE=local \ |
150 | 167 | -e STORAGE_PATH=/data/storage \ |
151 | - -e REPOS_PATH=/data/repos \ | |
152 | - -e SSH_HOST_KEY_PATH=/data/ssh/host_key \ | |
168 | + -e GIT_REPOS_PATH=/data/repos \ | |
169 | + -e GIT_SSH_HOST_KEY=/data/ssh/host_key \ | |
153 | 170 | -v /volume1/opencodehub/repos:/data/repos \ |
154 | 171 | -v /volume1/opencodehub/storage:/data/storage \ |
155 | 172 | -v /volume1/opencodehub/ssh:/data/ssh \ |
156 | 173 | -v /volume1/opencodehub/cache:/data/cache \ |
157 | - swadhinbiswas/opencodehub:latest | |
174 | + opencodehub/opencodehub:latest | |
158 | 175 | ``` |
159 | 176 | |
160 | 177 | > **Note:** On Synology, `host.docker.internal` may not work. Use your NAS IP address instead (e.g., `192.168.1.100`). |
178 | +> | |
179 | +> **SSH Git:** the app container serves the web UI only. For SSH access, | |
180 | +> run a second container from the same image with `PROCESS_TYPE=ssh` and | |
181 | +> expose port 2222 (this is exactly what `docker-compose.nas.yml` does). | |
161 | 182 | |
162 | 183 | #### Option B: Build from Source |
163 | 184 | |
| @@ -175,9 +196,9 @@ services: | ||
175 | 196 | restart: unless-stopped |
176 | 197 | ports: |
177 | 198 | - "4321:4321" |
178 | - - "2222:2222" | |
179 | 199 | environment: |
180 | 200 | - NODE_ENV=production |
201 | + - PROCESS_TYPE=app | |
181 | 202 | - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub |
182 | 203 | - REDIS_URL=redis://:YOUR_PASS@redis:6379 |
183 | 204 | - JWT_SECRET=YOUR_SECRET |
| @@ -186,11 +207,72 @@ services: | ||
186 | 207 | - CRON_SECRET=YOUR_SECRET |
187 | 208 | - RUNNER_SECRET=YOUR_SECRET |
188 | 209 | - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET |
210 | + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET | |
189 | 211 | - SITE_URL=https://git.yourddns.net |
190 | 212 | - STORAGE_TYPE=local |
191 | 213 | - STORAGE_PATH=/data/storage |
214 | + - GIT_REPOS_PATH=/data/repos | |
215 | + - REPOS_PATH=/data/repos | |
216 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
217 | + - GIT_SSH_PORT=2222 | |
218 | + volumes: | |
219 | + - /volume1/opencodehub/repos:/data/repos | |
220 | + - /volume1/opencodehub/storage:/data/storage | |
221 | + - /volume1/opencodehub/ssh:/data/ssh | |
222 | + - /volume1/opencodehub/cache:/data/cache | |
223 | + depends_on: | |
224 | + - postgres | |
225 | + - redis | |
226 | + networks: | |
227 | + - och-net | |
228 | + | |
229 | + ssh-git: | |
230 | + build: | |
231 | + context: /volume1/docker/OpenCodeHub | |
232 | + dockerfile: Dockerfile | |
233 | + container_name: opencodehub-ssh | |
234 | + restart: unless-stopped | |
235 | + ports: | |
236 | + - "2222:2222" | |
237 | + environment: | |
238 | + - NODE_ENV=production | |
239 | + - PROCESS_TYPE=ssh | |
240 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
241 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
242 | + - JWT_SECRET=YOUR_SECRET | |
243 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
244 | + - GIT_REPOS_PATH=/data/repos | |
245 | + - REPOS_PATH=/data/repos | |
246 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
247 | + - GIT_SSH_PORT=2222 | |
248 | + volumes: | |
249 | + - /volume1/opencodehub/repos:/data/repos | |
250 | + - /volume1/opencodehub/storage:/data/storage | |
251 | + - /volume1/opencodehub/ssh:/data/ssh | |
252 | + - /volume1/opencodehub/cache:/data/cache | |
253 | + depends_on: | |
254 | + - postgres | |
255 | + - redis | |
256 | + networks: | |
257 | + - och-net | |
258 | + | |
259 | + worker: | |
260 | + build: | |
261 | + context: /volume1/docker/OpenCodeHub | |
262 | + dockerfile: Dockerfile | |
263 | + container_name: opencodehub-worker | |
264 | + restart: unless-stopped | |
265 | + environment: | |
266 | + - NODE_ENV=production | |
267 | + - PROCESS_TYPE=worker | |
268 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
269 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
270 | + - JWT_SECRET=YOUR_SECRET | |
271 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
272 | + - GIT_REPOS_PATH=/data/repos | |
192 | 273 | - REPOS_PATH=/data/repos |
193 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
274 | + - STORAGE_TYPE=local | |
275 | + - STORAGE_PATH=/data/storage | |
194 | 276 | volumes: |
195 | 277 | - /volume1/opencodehub/repos:/data/repos |
196 | 278 | - /volume1/opencodehub/storage:/data/storage |
| @@ -301,14 +383,14 @@ version: '3.8' | ||
301 | 383 | |
302 | 384 | services: |
303 | 385 | app: |
304 | - image: swadhinbiswas/opencodehub:latest | |
386 | + image: opencodehub/opencodehub:latest | |
305 | 387 | container_name: opencodehub |
306 | 388 | restart: unless-stopped |
307 | 389 | ports: |
308 | 390 | - "4321:4321" |
309 | - - "2222:2222" | |
310 | 391 | environment: |
311 | 392 | - NODE_ENV=production |
393 | + - PROCESS_TYPE=app | |
312 | 394 | - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub |
313 | 395 | - REDIS_URL=redis://:YOUR_PASS@redis:6379 |
314 | 396 | - JWT_SECRET=YOUR_SECRET |
| @@ -317,11 +399,68 @@ services: | ||
317 | 399 | - CRON_SECRET=YOUR_SECRET |
318 | 400 | - RUNNER_SECRET=YOUR_SECRET |
319 | 401 | - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET |
402 | + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET | |
320 | 403 | - SITE_URL=https://git.yourdomain.com |
321 | 404 | - STORAGE_TYPE=local |
322 | 405 | - STORAGE_PATH=/data/storage |
406 | + - GIT_REPOS_PATH=/data/repos | |
407 | + - REPOS_PATH=/data/repos | |
408 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
409 | + - GIT_SSH_PORT=2222 | |
410 | + volumes: | |
411 | + - /mnt/YOURPOOL/opencodehub/repos:/data/repos | |
412 | + - /mnt/YOURPOOL/opencodehub/storage:/data/storage | |
413 | + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh | |
414 | + - /mnt/YOURPOOL/opencodehub/cache:/data/cache | |
415 | + depends_on: | |
416 | + - postgres | |
417 | + - redis | |
418 | + networks: | |
419 | + - och-net | |
420 | + | |
421 | + ssh-git: | |
422 | + image: opencodehub/opencodehub:latest | |
423 | + container_name: opencodehub-ssh | |
424 | + restart: unless-stopped | |
425 | + ports: | |
426 | + - "2222:2222" | |
427 | + environment: | |
428 | + - NODE_ENV=production | |
429 | + - PROCESS_TYPE=ssh | |
430 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
431 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
432 | + - JWT_SECRET=YOUR_SECRET | |
433 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
434 | + - GIT_REPOS_PATH=/data/repos | |
435 | + - REPOS_PATH=/data/repos | |
436 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
437 | + - GIT_SSH_PORT=2222 | |
438 | + volumes: | |
439 | + - /mnt/YOURPOOL/opencodehub/repos:/data/repos | |
440 | + - /mnt/YOURPOOL/opencodehub/storage:/data/storage | |
441 | + - /mnt/YOURPOOL/opencodehub/ssh:/data/ssh | |
442 | + - /mnt/YOURPOOL/opencodehub/cache:/data/cache | |
443 | + depends_on: | |
444 | + - postgres | |
445 | + - redis | |
446 | + networks: | |
447 | + - och-net | |
448 | + | |
449 | + worker: | |
450 | + image: opencodehub/opencodehub:latest | |
451 | + container_name: opencodehub-worker | |
452 | + restart: unless-stopped | |
453 | + environment: | |
454 | + - NODE_ENV=production | |
455 | + - PROCESS_TYPE=worker | |
456 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
457 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
458 | + - JWT_SECRET=YOUR_SECRET | |
459 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
460 | + - GIT_REPOS_PATH=/data/repos | |
323 | 461 | - REPOS_PATH=/data/repos |
324 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
462 | + - STORAGE_TYPE=local | |
463 | + - STORAGE_PATH=/data/storage | |
325 | 464 | volumes: |
326 | 465 | - /mnt/YOURPOOL/opencodehub/repos:/data/repos |
327 | 466 | - /mnt/YOURPOOL/opencodehub/storage:/data/storage |
| @@ -443,7 +582,7 @@ Install Nginx Proxy Manager from the TrueNAS Apps catalog: | ||
443 | 582 | |
444 | 583 | #### Create OpenCodeHub: |
445 | 584 | |
446 | -1. Search and install `swadhinbiswas/opencodehub:latest` | |
585 | +1. Search and install `opencodehub/opencodehub:latest` | |
447 | 586 | 2. Name: `opencodehub` |
448 | 587 | 3. Network: `opencodehub-network` |
449 | 588 | 4. Environment variables (same as Synology example) |
| @@ -496,14 +635,14 @@ version: '3.8' | ||
496 | 635 | |
497 | 636 | services: |
498 | 637 | app: |
499 | - image: swadhinbiswas/opencodehub:latest | |
638 | + image: opencodehub/opencodehub:latest | |
500 | 639 | container_name: opencodehub |
501 | 640 | restart: unless-stopped |
502 | 641 | ports: |
503 | 642 | - "4321:4321" |
504 | - - "2222:2222" | |
505 | 643 | environment: |
506 | 644 | - NODE_ENV=production |
645 | + - PROCESS_TYPE=app | |
507 | 646 | - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub |
508 | 647 | - REDIS_URL=redis://:YOUR_PASS@redis:6379 |
509 | 648 | - JWT_SECRET=YOUR_SECRET |
| @@ -512,11 +651,68 @@ services: | ||
512 | 651 | - CRON_SECRET=YOUR_SECRET |
513 | 652 | - RUNNER_SECRET=YOUR_SECRET |
514 | 653 | - WORKFLOW_SECRET_ENCRYPTION_KEY=YOUR_SECRET |
654 | + - AI_CONFIG_ENCRYPTION_KEY=YOUR_SECRET | |
515 | 655 | - SITE_URL=https://git.yourdomain.com |
516 | 656 | - STORAGE_TYPE=local |
517 | 657 | - STORAGE_PATH=/data/storage |
658 | + - GIT_REPOS_PATH=/data/repos | |
659 | + - REPOS_PATH=/data/repos | |
660 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
661 | + - GIT_SSH_PORT=2222 | |
662 | + volumes: | |
663 | + - ./repos:/data/repos | |
664 | + - ./storage:/data/storage | |
665 | + - ./ssh:/data/ssh | |
666 | + - ./cache:/data/cache | |
667 | + depends_on: | |
668 | + - postgres | |
669 | + - redis | |
670 | + networks: | |
671 | + - och-net | |
672 | + | |
673 | + ssh-git: | |
674 | + image: opencodehub/opencodehub:latest | |
675 | + container_name: opencodehub-ssh | |
676 | + restart: unless-stopped | |
677 | + ports: | |
678 | + - "2222:2222" | |
679 | + environment: | |
680 | + - NODE_ENV=production | |
681 | + - PROCESS_TYPE=ssh | |
682 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
683 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
684 | + - JWT_SECRET=YOUR_SECRET | |
685 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
686 | + - GIT_REPOS_PATH=/data/repos | |
687 | + - REPOS_PATH=/data/repos | |
688 | + - GIT_SSH_HOST_KEY=/data/ssh/host_key | |
689 | + - GIT_SSH_PORT=2222 | |
690 | + volumes: | |
691 | + - ./repos:/data/repos | |
692 | + - ./storage:/data/storage | |
693 | + - ./ssh:/data/ssh | |
694 | + - ./cache:/data/cache | |
695 | + depends_on: | |
696 | + - postgres | |
697 | + - redis | |
698 | + networks: | |
699 | + - och-net | |
700 | + | |
701 | + worker: | |
702 | + image: opencodehub/opencodehub:latest | |
703 | + container_name: opencodehub-worker | |
704 | + restart: unless-stopped | |
705 | + environment: | |
706 | + - NODE_ENV=production | |
707 | + - PROCESS_TYPE=worker | |
708 | + - DATABASE_URL=postgresql://opencodehub:YOUR_PASS@postgres:5432/opencodehub | |
709 | + - REDIS_URL=redis://:YOUR_PASS@redis:6379 | |
710 | + - JWT_SECRET=YOUR_SECRET | |
711 | + - INTERNAL_HOOK_SECRET=YOUR_SECRET | |
712 | + - GIT_REPOS_PATH=/data/repos | |
518 | 713 | - REPOS_PATH=/data/repos |
519 | - - SSH_HOST_KEY_PATH=/data/ssh/host_key | |
714 | + - STORAGE_TYPE=local | |
715 | + - STORAGE_PATH=/data/storage | |
520 | 716 | volumes: |
521 | 717 | - ./repos:/data/repos |
522 | 718 | - ./storage:/data/storage |
523 | 719 | |
▼docs/administration/deployment.md
+40-36
| @@ -116,9 +116,6 @@ STORAGE_SECRET_ACCESS_KEY=<your-secret-key> | ||
116 | 116 | |
117 | 117 | **Optional but Recommended:** |
118 | 118 | ```bash |
119 | -# Error Monitoring | |
120 | -SENTRY_DSN=https://your-sentry-dsn | |
121 | - | |
122 | 119 | # Email |
123 | 120 | SMTP_HOST=smtp.example.com |
124 | 121 | SMTP_PORT=587 |
| @@ -127,7 +124,7 @@ SMTP_PASSWORD=<smtp-password> | ||
127 | 124 | |
128 | 125 | |
129 | 126 | # Redis (for session management) |
130 | -REDIS_URL=redis://localhost:6379 | |
127 | +REDIS_URL=redis://:your-password@localhost:6379 | |
131 | 128 | ``` |
132 | 129 | |
133 | 130 | ### Deployment Steps |
| @@ -138,21 +135,19 @@ git clone https://github.com/swadhinbiswas/OpencodeHub.git | ||
138 | 135 | cd OpenCodeHub |
139 | 136 | |
140 | 137 | # 2. Install dependencies |
141 | -npm install # or bun install | |
138 | +bun install # or npm install | |
142 | 139 | |
143 | 140 | # 3. Create production .env |
144 | 141 | cp .env.example .env |
145 | -nano .env # Configure all production values | |
142 | +nano .env # Configure all production values (secrets: openssl rand -hex 32) | |
146 | 143 | |
147 | 144 | # 4. Validate environment |
148 | 145 | bun run src/lib/env-validation.ts |
149 | 146 | # Should output: ✅ Environment validation passed |
150 | 147 | |
151 | -# 5. Run database migrations | |
152 | -npm run db:push | |
153 | -# or generate versioned migrations: | |
154 | -npm run db:generate | |
155 | -npm run db:migrate | |
148 | +# 5. Run database migrations (applies committed drizzle/ migrations) | |
149 | +bun run migrate | |
150 | +# or for dev: npm run db:push | |
156 | 151 | |
157 | 152 | # 6. Create admin user |
158 | 153 | bun run scripts/seed-admin.ts |
| @@ -161,59 +156,68 @@ bun run scripts/seed-admin.ts | ||
161 | 156 | # 7. Build for production |
162 | 157 | npm run build |
163 | 158 | |
164 | -# 8. Start production server | |
165 | -npm start | |
159 | +# 8. Start the production server (built in step 7) | |
160 | +npm start # → node ./dist/server/entry.mjs | |
166 | 161 | ``` |
167 | 162 | |
163 | +> **Docker** is the recommended path for production. Use the repository's | |
164 | +> `docker-compose.yml` (quickstart) or `docker-compose.production.yml` | |
165 | +> (Caddy TLS + isolated networks) — do not hand-roll a compose file. | |
166 | + | |
168 | 167 | ### Docker Deployment (Recommended) |
169 | 168 | |
170 | -```yaml | |
171 | -# docker-compose.prod.yml | |
172 | -version: '3.8' | |
169 | +Use the repository's own compose files — they are kept in sync with the | |
170 | +codebase and are production-safe (no default secrets, migrations applied | |
171 | +at boot, worker + SSH services included): | |
172 | + | |
173 | +```bash | |
174 | +# Quickstart (app + SSH + worker + PostgreSQL + Redis) | |
175 | +cp .env.example .env # then set ALL secrets | |
176 | +docker compose up -d | |
177 | + | |
178 | +# Production profile (adds Caddy TLS termination + isolated networks) | |
179 | +docker compose -f docker-compose.production.yml --profile production up -d | |
180 | + | |
181 | +# NAS / dedicated-disk deployments | |
182 | +OCH_DATA_DIR=/mnt/nas/opencodehub docker compose -f docker-compose.nas.yml up -d | |
183 | +``` | |
184 | + | |
185 | +Minimal reference compose (for illustration only): | |
173 | 186 | |
187 | +```yaml | |
174 | 188 | services: |
175 | 189 | app: |
176 | 190 | build: . |
177 | 191 | restart: always |
178 | 192 | ports: |
179 | - - "3000:3000" | |
180 | - env_file: .env.production | |
193 | + - "4321:4321" | |
194 | + env_file: .env | |
195 | + environment: | |
196 | + NODE_ENV: production | |
181 | 197 | depends_on: |
182 | 198 | - postgres |
183 | 199 | - redis |
184 | - volumes: | |
185 | - - ./data:/app/data | |
186 | - environment: | |
187 | - NODE_ENV: production | |
188 | - healthcheck: | |
189 | - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] | |
190 | - interval: 30s | |
191 | - timeout: 10s | |
192 | - retries: 3 | |
193 | 200 | |
194 | 201 | postgres: |
195 | - image: postgres:15-alpine | |
202 | + image: postgres:16-alpine | |
196 | 203 | restart: always |
197 | 204 | environment: |
198 | 205 | POSTGRES_DB: opencodehub |
199 | 206 | POSTGRES_USER: opencodehub |
200 | 207 | POSTGRES_PASSWORD: ${DATABASE_PASSWORD} |
201 | - volumes: | |
202 | - - postgres_data:/var/lib/postgresql/data | |
203 | - ports: | |
204 | - - "5432:5432" | |
205 | 208 | |
206 | 209 | redis: |
207 | 210 | image: redis:7-alpine |
208 | 211 | restart: always |
209 | 212 | command: redis-server --requirepass ${REDIS_PASSWORD} |
210 | - ports: | |
211 | - - "6379:6379" | |
212 | 213 | |
213 | 214 | volumes: |
214 | 215 | postgres_data: |
215 | 216 | ``` |
216 | 217 | |
218 | +> Exposed ports: **4321** (web/API/Git HTTP) and **2222** (SSH Git). | |
219 | +> PostgreSQL (5432) and Redis (6379) stay on an internal network. | |
220 | + | |
217 | 221 | ```bash |
218 | 222 | # Deploy with Docker |
219 | 223 | docker-compose -f docker-compose.prod.yml up -d |
| @@ -244,7 +248,7 @@ server { | ||
244 | 248 | |
245 | 249 | # Proxy configuration |
246 | 250 | location / { |
247 | - proxy_pass http://localhost:3000; | |
251 | + proxy_pass http://localhost:4321; | |
248 | 252 | proxy_set_header Host $host; |
249 | 253 | proxy_set_header X-Real-IP $remote_addr; |
250 | 254 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| @@ -258,7 +262,7 @@ server { | ||
258 | 262 | |
259 | 263 | # Git operations (larger timeouts) |
260 | 264 | location ~ ^/[^/]+/[^/]+\.git/ { |
261 | - proxy_pass http://localhost:3000; | |
265 | + proxy_pass http://localhost:4321; | |
262 | 266 | proxy_read_timeout 600s; |
263 | 267 | proxy_send_timeout 600s; |
264 | 268 | proxy_buffer_size 128k; |
265 | 269 | |
▼docs/administration/expose-nas.md
+9-9
| @@ -43,7 +43,7 @@ firewall never sees an inbound connection to OpenCodeHub ports. | ||
43 | 43 | |
44 | 44 | Whatever exposure method you pick, you need: |
45 | 45 | |
46 | -1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `3000`). | |
46 | +1. **A working OpenCodeHub instance** on your NAS, listening on a local port (default `4321`). | |
47 | 47 | 2. **A reachable hostname** (Tailscale or Cloudflare) pointed at the tunnel. |
48 | 48 | 3. **A persistent `cloudflared` or `tailscaled`** process — running on the NAS, in Docker, or as a system service. |
49 | 49 | 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 | ||
105 | 105 | # in the Tailscale admin console: https://login.tailscale.com/admin |
106 | 106 | # (turn on the "Funnel" feature flag). |
107 | 107 | # |
108 | -# Then on the NAS, expose the port OpenCodeHub listens on (default 3000): | |
109 | -sudo tailscale funnel 3000 on | |
108 | +# Then on the NAS, expose the port OpenCodeHub listens on (default 4321): | |
109 | +sudo tailscale funnel 4321 on | |
110 | 110 | |
111 | 111 | # Or expose a specific path with HTTPS-only: |
112 | -sudo tailscale funnel --https=443 http://localhost:3000 | |
112 | +sudo tailscale funnel --https=443 http://localhost:4321 | |
113 | 113 | ``` |
114 | 114 | |
115 | 115 | 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 | ||
137 | 137 | run: |
138 | 138 | |
139 | 139 | ```bash |
140 | -sudo tailscale funnel 3000 on | |
140 | +sudo tailscale funnel 4321 on | |
141 | 141 | ``` |
142 | 142 | |
143 | 143 | For TrueNAS SCALE, configure the Tailscale app's "Post-Start" command |
| @@ -267,11 +267,11 @@ In the Cloudflare dashboard → your tunnel → **Public Hostname**: | ||
267 | 267 | |---|---| |
268 | 268 | | Subdomain | `git` (or anything) | |
269 | 269 | | Domain | `example.com` | |
270 | -| **Service** | Type: `HTTP`, URL: `opencodehub:3000` (or `localhost:3000` if `cloudflared` is host-networking) | | |
270 | +| **Service** | Type: `HTTP`, URL: `opencodehub:4321` (or `localhost:4321` if `cloudflared` is host-networking) | | |
271 | 271 | |
272 | 272 | If you used Option A (Docker bridge network), `opencodehub` must be the |
273 | 273 | service name in your OpenCodeHub `docker-compose.yml`. If you used |
274 | -host networking, use `localhost:3000`. | |
274 | +host networking, use `localhost:4321`. | |
275 | 275 | |
276 | 276 | Save. After a few seconds, `https://git.example.com` should reach your |
277 | 277 | OpenCodeHub instance. |
| @@ -383,8 +383,8 @@ on the NAS itself. | ||
383 | 383 | - Cloudflare: the tunnel status is **HEALTHY** in the Zero Trust |
384 | 384 | dashboard. |
385 | 385 | - Confirm OpenCodeHub is reachable from the tunnel's network namespace: |
386 | - - Tailscale (host networking): `curl http://localhost:3000` | |
387 | - - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:3000/` | |
386 | + - Tailscale (host networking): `curl http://localhost:4321` | |
387 | + - Cloudflare (Docker bridge): `docker exec cloudflared wget -qO- http://opencodehub:4321/` | |
388 | 388 | |
389 | 389 | ### Real client IP is wrong |
390 | 390 | |
391 | 391 | |
▼docs/administration/horizontal-scaling.md
+17-0
| @@ -0,0 +1,17 @@ | ||
1 | +# Horizontal Scaling | |
2 | + | |
3 | +OpenCodeHub is designed to be scaled horizontally to handle high traffic and large workloads. | |
4 | + | |
5 | +## Stateless Application Servers | |
6 | +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). | |
7 | + | |
8 | +## State Management | |
9 | +- **Database**: Ensure your database (PostgreSQL) is appropriately scaled or managed (e.g., AWS RDS). | |
10 | +- **Redis**: Redis is strictly required for horizontal scaling. It handles sessions, rate limiting, distributed locking, and BullMQ queues for the background workers. | |
11 | +- **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. | |
12 | + | |
13 | +## Shared Git Storage | |
14 | +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. | |
15 | + | |
16 | +## Pluggable Blob Storage | |
17 | +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. | |
0 | 18 | |
▼docs/administration/incident-runbook.md
+1-1
| @@ -48,7 +48,7 @@ curl -s https://your-domain.com/api/metrics | grep active_runners | ||
48 | 48 | kubectl set env deployment/opencodehub FEATURE_X_DISABLED=true |
49 | 49 | |
50 | 50 | # Option B: Rollback (Docker) |
51 | -docker run --rm swadhinbiswas/opencodehub:previous-tag | |
51 | +docker run --rm opencodehub/opencodehub:previous-tag | |
52 | 52 | # Update docker-compose.yml with previous tag |
53 | 53 | docker compose -f docker-compose.production.yml up -d |
54 | 54 | |
55 | 55 | |
▼docs/administration/known-accepted-vulns.json
+15-0
| @@ -1,8 +1,23 @@ | ||
1 | 1 | { |
2 | 2 | "_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.", |
3 | 3 | "accepted": [ |
4 | + "@astrojs/vercel", | |
5 | + "@opentelemetry/propagator-jaeger", | |
6 | + "@opentelemetry/sdk-node", | |
7 | + "@vercel/nft", | |
4 | 8 | "astro", |
9 | + "brace-expansion", | |
10 | + "cacache", | |
11 | + "gaxios", | |
12 | + "gcp-metadata", | |
13 | + "glob", | |
14 | + "make-fetch-happen", | |
15 | + "minimatch", | |
16 | + "node-gyp", | |
17 | + "nodegit", | |
5 | 18 | "nodemailer", |
19 | + "rimraf", | |
20 | + "sharp", | |
6 | 21 | "undici", |
7 | 22 | "vite" |
8 | 23 | ], |
9 | 24 | |
▼docs/administration/monitoring.md
+2-0
| @@ -8,6 +8,8 @@ The minimum production observability stack should include: | ||
8 | 8 | |
9 | 9 | - `GET /api/health` for liveness/readiness |
10 | 10 | - `GET /api/metrics` for Prometheus scraping |
11 | + (protected by `METRICS_TOKEN` when set — pass it as a Bearer token in the | |
12 | + scrape config; see `deploy/prometheus.yml`) | |
11 | 13 | - Grafana dashboards for request latency, queue health, and storage/DB timing |
12 | 14 | - Alerting for p95 regressions and service degradation |
13 | 15 | |
14 | 16 | |
▼docs/cli.md
+694-0
| @@ -0,0 +1,694 @@ | ||
1 | +# OpenCodeHub CLI (och) | |
2 | + | |
3 | +The OpenCodeHub CLI (`och`) provides a GitHub CLI-like experience for interacting with OpenCodeHub from the command line. | |
4 | + | |
5 | +## Installation | |
6 | + | |
7 | +```bash | |
8 | +npm install -g opencodehub-cli | |
9 | +``` | |
10 | + | |
11 | +## Quick Start | |
12 | + | |
13 | +```bash | |
14 | +# 1. Login to your server | |
15 | +och auth login --url http://your-server:4321 | |
16 | + | |
17 | +# 2. Create a repository | |
18 | +och repo create my-project --description "My new project" | |
19 | + | |
20 | +# 3. Push your code | |
21 | +och repo push --branch main | |
22 | +``` | |
23 | + | |
24 | +--- | |
25 | + | |
26 | +## Authentication | |
27 | + | |
28 | +### `och auth login` | |
29 | +Login to OpenCodeHub. | |
30 | + | |
31 | +```bash | |
32 | +och auth login --url http://your-server:4321 | |
33 | +``` | |
34 | + | |
35 | +**Options:** | |
36 | +| Option | Description | | |
37 | +|--------|-------------| | |
38 | +| `-u, --url <url>` | OpenCodeHub server URL | | |
39 | +| `-t, --token <token>` | Personal access token (non-interactive) | | |
40 | +| `--with-token` | Authenticate using interactive token entry | | |
41 | +| `--ca-file <path>` | Path to custom CA bundle (self-signed TLS) | | |
42 | +| `--insecure` | Disable TLS certificate verification | | |
43 | + | |
44 | +**Examples:** | |
45 | +```bash | |
46 | +# Interactive login | |
47 | +och auth login --url http://localhost:4321 | |
48 | + | |
49 | +# Non-interactive with token | |
50 | +och auth login --url http://localhost:4321 -t och_xxxxx | |
51 | + | |
52 | +# Self-signed TLS | |
53 | +och auth login --url https://git.example.com --ca-file /path/to/ca.pem | |
54 | +``` | |
55 | + | |
56 | +### `och auth logout` | |
57 | +Logout from OpenCodeHub. | |
58 | + | |
59 | +```bash | |
60 | +och auth logout | |
61 | +``` | |
62 | + | |
63 | +### `och auth status` | |
64 | +Show current authentication status. | |
65 | + | |
66 | +```bash | |
67 | +och auth status | |
68 | +``` | |
69 | + | |
70 | +--- | |
71 | + | |
72 | +## Repository Management | |
73 | + | |
74 | +### `och repo create <name>` | |
75 | +Create a new repository on OpenCodeHub. | |
76 | + | |
77 | +```bash | |
78 | +och repo create my-project --description "A cool project" | |
79 | +``` | |
80 | + | |
81 | +**Options:** | |
82 | +| Option | Description | | |
83 | +|--------|-------------| | |
84 | +| `-d, --description <desc>` | Repository description | | |
85 | +| `-p, --private` | Make repository private | | |
86 | +| `--no-init` | Don't initialize with README | | |
87 | + | |
88 | +### `och repo clone <repo>` | |
89 | +Clone a repository from OpenCodeHub. | |
90 | + | |
91 | +```bash | |
92 | +och repo clone owner/repo | |
93 | +och repo clone owner/repo my-local-name | |
94 | +``` | |
95 | + | |
96 | +**Arguments:** | |
97 | +| Argument | Description | | |
98 | +|----------|-------------| | |
99 | +| `<repo>` | Repository in `owner/name` format | | |
100 | +| `[destination]` | Local directory name (optional) | | |
101 | + | |
102 | +### `och repo push` | |
103 | +Push local repository to OpenCodeHub. | |
104 | + | |
105 | +```bash | |
106 | +och repo push | |
107 | +och repo push --branch main | |
108 | +och repo push --force | |
109 | +``` | |
110 | + | |
111 | +**Options:** | |
112 | +| Option | Description | | |
113 | +|--------|-------------| | |
114 | +| `-b, --branch <branch>` | Branch to push | | |
115 | +| `-f, --force` | Force push (overwrite remote) | | |
116 | + | |
117 | +### `och repo list` | |
118 | +List your repositories. | |
119 | + | |
120 | +```bash | |
121 | +och repo list | |
122 | +``` | |
123 | + | |
124 | +--- | |
125 | + | |
126 | +## Git Operations | |
127 | + | |
128 | +### `och git init` | |
129 | +Initialize a repository for OpenCodeHub. | |
130 | + | |
131 | +```bash | |
132 | +och git init | |
133 | +och git init --url http://your-server:4321 | |
134 | +``` | |
135 | + | |
136 | +**Options:** | |
137 | +| Option | Description | | |
138 | +|--------|-------------| | |
139 | +| `-u, --url <url>` | OpenCodeHub server URL | | |
140 | + | |
141 | +### `och git push` | |
142 | +Push to OpenCodeHub (shorthand for `repo push`). | |
143 | + | |
144 | +```bash | |
145 | +och git push | |
146 | +och git push --branch main --force | |
147 | +``` | |
148 | + | |
149 | +### `och git sync` | |
150 | +Sync with remote (fetch + push). | |
151 | + | |
152 | +```bash | |
153 | +och git sync | |
154 | +och git sync --force | |
155 | +``` | |
156 | + | |
157 | +**Options:** | |
158 | +| Option | Description | | |
159 | +|--------|-------------| | |
160 | +| `-f, --force` | Force push after sync | | |
161 | + | |
162 | +--- | |
163 | + | |
164 | +## Pull Requests | |
165 | + | |
166 | +### `och pr create` | |
167 | +Create a new pull request. | |
168 | + | |
169 | +```bash | |
170 | +och pr create | |
171 | +och pr create --title "feat: new feature" --base main | |
172 | +``` | |
173 | + | |
174 | +**Options:** | |
175 | +| Option | Description | | |
176 | +|--------|-------------| | |
177 | +| `-t, --title <title>` | PR title | | |
178 | +| `-b, --base <branch>` | Base branch | | |
179 | +| `-h, --head <branch>` | Head branch | | |
180 | +| `-d, --description <desc>` | PR description | | |
181 | + | |
182 | +### `och pr list` | |
183 | +List pull requests. | |
184 | + | |
185 | +```bash | |
186 | +och pr list | |
187 | +och pr list --state open | |
188 | +``` | |
189 | + | |
190 | +**Options:** | |
191 | +| Option | Description | | |
192 | +|--------|-------------| | |
193 | +| `-s, --state <state>` | Filter by state (open, closed, all) | | |
194 | + | |
195 | +### `och pr view <number>` | |
196 | +View a pull request. | |
197 | + | |
198 | +```bash | |
199 | +och pr view 42 | |
200 | +``` | |
201 | + | |
202 | +### `och pr checkout <number>` | |
203 | +Checkout a pull request locally. | |
204 | + | |
205 | +```bash | |
206 | +och pr checkout 42 | |
207 | +``` | |
208 | + | |
209 | +### `och pr merge <number>` | |
210 | +Merge a pull request. | |
211 | + | |
212 | +```bash | |
213 | +och pr merge 42 | |
214 | +``` | |
215 | + | |
216 | +### `och pr close <number>` | |
217 | +Close a pull request. | |
218 | + | |
219 | +```bash | |
220 | +och pr close 42 | |
221 | +``` | |
222 | + | |
223 | +--- | |
224 | + | |
225 | +## Stacked PRs | |
226 | + | |
227 | +### `och stack create <name>` | |
228 | +Create a new branch in the current stack. | |
229 | + | |
230 | +```bash | |
231 | +och stack create feature/child-1 | |
232 | +``` | |
233 | + | |
234 | +### `och stack submit` | |
235 | +Push stack and create/update PRs for all branches. | |
236 | + | |
237 | +```bash | |
238 | +och stack submit | |
239 | +``` | |
240 | + | |
241 | +### `och stack sync` | |
242 | +Rebase stack on updated main. | |
243 | + | |
244 | +```bash | |
245 | +och stack sync | |
246 | +``` | |
247 | + | |
248 | +### `och stack log` | |
249 | +Visualize stack tree. | |
250 | + | |
251 | +```bash | |
252 | +och stack log | |
253 | +``` | |
254 | + | |
255 | +### `och stack status` | |
256 | +Show current stack and branch status. | |
257 | + | |
258 | +```bash | |
259 | +och stack status | |
260 | +``` | |
261 | + | |
262 | +--- | |
263 | + | |
264 | +## Issues | |
265 | + | |
266 | +### `och issue create` | |
267 | +Create a new issue. | |
268 | + | |
269 | +```bash | |
270 | +och issue create --title "Bug: something broken" --body "Description..." | |
271 | +``` | |
272 | + | |
273 | +**Options:** | |
274 | +| Option | Description | | |
275 | +|--------|-------------| | |
276 | +| `-t, --title <title>` | Issue title | | |
277 | +| `-b, --body <body>` | Issue body | | |
278 | +| `--labels <labels>` | Comma-separated labels | | |
279 | + | |
280 | +### `och issue list` | |
281 | +List issues. | |
282 | + | |
283 | +```bash | |
284 | +och issue list | |
285 | +och issue list --state open | |
286 | +``` | |
287 | + | |
288 | +**Options:** | |
289 | +| Option | Description | | |
290 | +|--------|-------------| | |
291 | +| `-s, --state <state>` | Filter by state (open, closed, all) | | |
292 | + | |
293 | +### `och issue view <number>` | |
294 | +View an issue. | |
295 | + | |
296 | +```bash | |
297 | +och issue view 42 | |
298 | +``` | |
299 | + | |
300 | +### `och issue close <number>` | |
301 | +Close an issue. | |
302 | + | |
303 | +```bash | |
304 | +och issue close 42 | |
305 | +``` | |
306 | + | |
307 | +--- | |
308 | + | |
309 | +## Branches | |
310 | + | |
311 | +### `och branch checkout [branch]` | |
312 | +Checkout a branch (interactive if no branch specified). | |
313 | + | |
314 | +```bash | |
315 | +och branch checkout | |
316 | +och branch checkout main | |
317 | +``` | |
318 | + | |
319 | +### `och branch rename [old-name] <new-name>` | |
320 | +Rename a branch. | |
321 | + | |
322 | +```bash | |
323 | +och branch rename old-name new-name | |
324 | +``` | |
325 | + | |
326 | +--- | |
327 | + | |
328 | +## Merge Queue | |
329 | + | |
330 | +### `och queue add <pr-number>` | |
331 | +Add a pull request to the merge queue. | |
332 | + | |
333 | +```bash | |
334 | +och queue add 42 | |
335 | +``` | |
336 | + | |
337 | +### `och queue list` | |
338 | +List merge queue items. | |
339 | + | |
340 | +```bash | |
341 | +och queue list | |
342 | +``` | |
343 | + | |
344 | +### `och queue remove <pr-number>` | |
345 | +Remove a pull request from the merge queue. | |
346 | + | |
347 | +```bash | |
348 | +och queue remove 42 | |
349 | +``` | |
350 | + | |
351 | +--- | |
352 | + | |
353 | +## CI/CD | |
354 | + | |
355 | +### `och ci list` | |
356 | +List pipeline runs. | |
357 | + | |
358 | +```bash | |
359 | +och ci list | |
360 | +``` | |
361 | + | |
362 | +### `och ci view <run-id>` | |
363 | +View a pipeline run. | |
364 | + | |
365 | +```bash | |
366 | +och ci view run-123 | |
367 | +``` | |
368 | + | |
369 | +### `och ci cancel <run-id>` | |
370 | +Cancel a pipeline run. | |
371 | + | |
372 | +```bash | |
373 | +och ci cancel run-123 | |
374 | +``` | |
375 | + | |
376 | +--- | |
377 | + | |
378 | +## Releases | |
379 | + | |
380 | +### `och release create <tag>` | |
381 | +Create a new release. | |
382 | + | |
383 | +```bash | |
384 | +och release create v1.0.0 --name "Version 1.0.0" --body "Release notes..." | |
385 | +``` | |
386 | + | |
387 | +**Options:** | |
388 | +| Option | Description | | |
389 | +|--------|-------------| | |
390 | +| `-n, --name <name>` | Release name | | |
391 | +| `-b, --body <body>` | Release notes | | |
392 | +| `--draft` | Create as draft | | |
393 | +| `--prerelease` | Mark as pre-release | | |
394 | + | |
395 | +### `och release list` | |
396 | +List releases. | |
397 | + | |
398 | +```bash | |
399 | +och release list | |
400 | +``` | |
401 | + | |
402 | +--- | |
403 | + | |
404 | +## Code Review | |
405 | + | |
406 | +### `och review ai <pr-number>` | |
407 | +Trigger AI code review for a pull request. | |
408 | + | |
409 | +```bash | |
410 | +och review ai 42 | |
411 | +``` | |
412 | + | |
413 | +### `och review status <pr-number>` | |
414 | +Check AI review status. | |
415 | + | |
416 | +```bash | |
417 | +och review status 42 | |
418 | +``` | |
419 | + | |
420 | +--- | |
421 | + | |
422 | +## Secrets | |
423 | + | |
424 | +### `och secret set <name>` | |
425 | +Set a secret. | |
426 | + | |
427 | +```bash | |
428 | +och secret set MY_SECRET | |
429 | +``` | |
430 | + | |
431 | +### `och secret list` | |
432 | +List secrets. | |
433 | + | |
434 | +```bash | |
435 | +och secret list | |
436 | +``` | |
437 | + | |
438 | +### `och secret delete <name>` | |
439 | +Delete a secret. | |
440 | + | |
441 | +```bash | |
442 | +och secret delete MY_SECRET | |
443 | +``` | |
444 | + | |
445 | +--- | |
446 | + | |
447 | +## Notifications | |
448 | + | |
449 | +### `och notify list` | |
450 | +List your notifications. | |
451 | + | |
452 | +```bash | |
453 | +och notify list | |
454 | +``` | |
455 | + | |
456 | +### `och notify read [id]` | |
457 | +Mark notification(s) as read. | |
458 | + | |
459 | +```bash | |
460 | +och notify read | |
461 | +och notify read notification-id | |
462 | +``` | |
463 | + | |
464 | +--- | |
465 | + | |
466 | +## SSH Keys | |
467 | + | |
468 | +### `och ssh-key add` | |
469 | +Add an SSH key to your account. | |
470 | + | |
471 | +```bash | |
472 | +och ssh-key add | |
473 | +``` | |
474 | + | |
475 | +### `och ssh-key list` | |
476 | +List your SSH keys. | |
477 | + | |
478 | +```bash | |
479 | +och ssh-key list | |
480 | +``` | |
481 | + | |
482 | +### `och ssh-key delete <id>` | |
483 | +Delete an SSH key. | |
484 | + | |
485 | +```bash | |
486 | +och ssh-key delete key-id | |
487 | +``` | |
488 | + | |
489 | +--- | |
490 | + | |
491 | +## Search | |
492 | + | |
493 | +### `och search repos <query>` | |
494 | +Search repositories. | |
495 | + | |
496 | +```bash | |
497 | +och search repos "machine learning" | |
498 | +``` | |
499 | + | |
500 | +### `och search issues <query>` | |
501 | +Search issues. | |
502 | + | |
503 | +```bash | |
504 | +och search issues "bug fix" | |
505 | +``` | |
506 | + | |
507 | +--- | |
508 | + | |
509 | +## Insights | |
510 | + | |
511 | +### `och insights show` | |
512 | +Show your developer metrics. | |
513 | + | |
514 | +```bash | |
515 | +och insights show | |
516 | +``` | |
517 | + | |
518 | +### `och insights team` | |
519 | +Show team/repository metrics. | |
520 | + | |
521 | +```bash | |
522 | +och insights team | |
523 | +``` | |
524 | + | |
525 | +--- | |
526 | + | |
527 | +## Configuration | |
528 | + | |
529 | +### `och config list` | |
530 | +List all configuration values. | |
531 | + | |
532 | +```bash | |
533 | +och config list | |
534 | +``` | |
535 | + | |
536 | +### `och config get <key>` | |
537 | +Get a configuration value. | |
538 | + | |
539 | +```bash | |
540 | +och config get serverUrl | |
541 | +``` | |
542 | + | |
543 | +### `och config set <key> <value>` | |
544 | +Set a configuration value. | |
545 | + | |
546 | +```bash | |
547 | +och config set serverUrl http://localhost:4321 | |
548 | +``` | |
549 | + | |
550 | +--- | |
551 | + | |
552 | +## Automations | |
553 | + | |
554 | +### `och automate list` | |
555 | +List automation rules for current repository. | |
556 | + | |
557 | +```bash | |
558 | +och automate list | |
559 | +``` | |
560 | + | |
561 | +### `och automate create` | |
562 | +Create a new automation rule. | |
563 | + | |
564 | +```bash | |
565 | +och automate create | |
566 | +``` | |
567 | + | |
568 | +--- | |
569 | + | |
570 | +## Runner | |
571 | + | |
572 | +### `och runner config` | |
573 | +Configure a self-hosted runner interactively. | |
574 | + | |
575 | +```bash | |
576 | +och runner config | |
577 | +``` | |
578 | + | |
579 | +--- | |
580 | + | |
581 | +## API | |
582 | + | |
583 | +### `och api <endpoint>` | |
584 | +Make direct API requests. | |
585 | + | |
586 | +```bash | |
587 | +och api /api/user | |
588 | +och api /api/repos --method POST --data '{"name": "my-repo"}' | |
589 | +``` | |
590 | + | |
591 | +**Options:** | |
592 | +| Option | Description | | |
593 | +|--------|-------------| | |
594 | +| `-m, --method <method>` | HTTP method (GET, POST, PUT, DELETE) | | |
595 | +| `-d, --data <data>` | Request body (JSON) | | |
596 | + | |
597 | +--- | |
598 | + | |
599 | +## Focus Mode | |
600 | + | |
601 | +### `och focus` | |
602 | +Interactive stack and review cockpit. | |
603 | + | |
604 | +```bash | |
605 | +och focus | |
606 | +``` | |
607 | + | |
608 | +A terminal UI for managing PRs, reviews, and merge queue. | |
609 | + | |
610 | +--- | |
611 | + | |
612 | +## Shell Completions | |
613 | + | |
614 | +### `och completion` | |
615 | +Generate shell completions. | |
616 | + | |
617 | +```bash | |
618 | +# Bash | |
619 | +och completion > ~/.bashrc.d/och-completion.bash | |
620 | + | |
621 | +# Zsh | |
622 | +och completion > ~/.zsh/completions/_och | |
623 | + | |
624 | +# Fish | |
625 | +och completion --fish > ~/.config/fish/completions/och.fish | |
626 | +``` | |
627 | + | |
628 | +--- | |
629 | + | |
630 | +## Environment Variables | |
631 | + | |
632 | +| Variable | Description | | |
633 | +|----------|-------------| | |
634 | +| `OCH_TOKEN` | Personal access token (overrides config) | | |
635 | +| `OCH_SERVER_URL` | Server URL (overrides config) | | |
636 | +| `OCH_HTTP_TIMEOUT_MS` | HTTP request timeout (default: 15000) | | |
637 | + | |
638 | +--- | |
639 | + | |
640 | +## Configuration File | |
641 | + | |
642 | +The CLI stores configuration in `~/.config/opencodehub-cli/config.json`: | |
643 | + | |
644 | +```json | |
645 | +{ | |
646 | + "serverUrl": "http://localhost:4321", | |
647 | + "token": "och_xxxxx", | |
648 | + "username": "your-username" | |
649 | +} | |
650 | +``` | |
651 | + | |
652 | +--- | |
653 | + | |
654 | +## Common Workflows | |
655 | + | |
656 | +### New Repository | |
657 | +```bash | |
658 | +och auth login --url http://your-server:4321 | |
659 | +mkdir my-project && cd my-project | |
660 | +git init | |
661 | +och repo create my-project --description "My project" | |
662 | +# Add files and commit | |
663 | +och repo push --branch main | |
664 | +``` | |
665 | + | |
666 | +### Daily Development | |
667 | +```bash | |
668 | +och git sync # Pull latest changes | |
669 | +och branch checkout feature-x # Switch branch | |
670 | +# Make changes and commit | |
671 | +och git push # Push changes | |
672 | +och pr create --title "feat: X" # Create PR | |
673 | +``` | |
674 | + | |
675 | +### Stacked PRs | |
676 | +```bash | |
677 | +och stack create feature/part-1 # Create first branch | |
678 | +# Work on part 1 | |
679 | +och stack submit # Push & create PR | |
680 | + | |
681 | +och stack create feature/part-2 # Create child branch | |
682 | +# Work on part 2 | |
683 | +och stack submit # Push & create PR | |
684 | + | |
685 | +och stack log # View stack hierarchy | |
686 | +``` | |
687 | + | |
688 | +### Code Review | |
689 | +```bash | |
690 | +och pr list # See open PRs | |
691 | +och pr checkout 42 # Checkout PR branch | |
692 | +# Review code | |
693 | +och pr merge 42 # Merge when ready | |
694 | +``` | |
0 | 695 | |
▼docs/development/contributing.md
+1-1
| @@ -49,7 +49,7 @@ Start the development server: | ||
49 | 49 | bun dev |
50 | 50 | ``` |
51 | 51 | |
52 | -Visit `http://localhost:3000`. | |
52 | +Visit `http://localhost:4321`. | |
53 | 53 | |
54 | 54 | --- |
55 | 55 | |
56 | 56 | |
▼docs/development/release-gates.md
+21-0
| @@ -0,0 +1,21 @@ | ||
1 | +# Release Gates | |
2 | + | |
3 | +This document outlines the required gates that must be passed before any code can be merged into the default branch. | |
4 | + | |
5 | +## 1. Continuous Integration (CI) | |
6 | +All pull requests must pass the following CI checks: | |
7 | +- **Linting**: `npm run lint` must exit with 0. | |
8 | +- **Typechecking**: `npm run typecheck` must exit with 0. | |
9 | +- **Tests**: Unit, integration, and E2E tests (`npm run test`) must all pass. | |
10 | + | |
11 | +## 2. Security Scans | |
12 | +- **Secret Scanning**: Commits must be scanned for leaked secrets. | |
13 | +- **Dependency Audit**: `npm audit` must not report vulnerabilities above the defined threshold. | |
14 | +- **Authorization Regression**: Core RBAC and permissions tests must pass. | |
15 | + | |
16 | +## 3. Documentation Parity | |
17 | +Any feature addition must include updates to both documentation stacks: | |
18 | +- The Markdown docs in `docs/` | |
19 | +- The Starlight site in `docs-site/src/content/docs/` | |
20 | + | |
21 | +Features will not be merged if their documentation is missing or incomplete. | |
0 | 22 | |
▼docs/getting-started/installation.md
+2-2
| @@ -47,7 +47,7 @@ INTERNAL_HOOK_SECRET=<openssl rand -hex 32> | ||
47 | 47 | |
48 | 48 | # Domain Configuration |
49 | 49 | SITE_URL=https://git.yourcompany.com |
50 | -PORT=3000 | |
50 | +PORT=4321 | |
51 | 51 | |
52 | 52 | # Database (Using the Postgres container defined in compose) |
53 | 53 | DATABASE_URL=postgresql://opencodehub:securepassword@postgres:5432/opencodehub |
| @@ -98,7 +98,7 @@ server { | ||
98 | 98 | client_max_body_size 500M; |
99 | 99 | |
100 | 100 | location / { |
101 | - proxy_pass http://localhost:3000; | |
101 | + proxy_pass http://localhost:4321; | |
102 | 102 | proxy_set_header Host $host; |
103 | 103 | proxy_set_header X-Real-IP $remote_addr; |
104 | 104 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
105 | 105 | |
▼docs/getting-started/quick-start.md
+1-1
| @@ -3,7 +3,7 @@ | ||
3 | 3 | Welcome to OpenCodeHub! This guide will help you navigate the platform and start collaborating in minutes. |
4 | 4 | |
5 | 5 | ## 1. Login |
6 | -Navigate to your instance URL (e.g., `http://localhost:3000`) and log in with the admin credentials you created during installation. | |
6 | +Navigate to your instance URL (e.g., `http://localhost:4321`) and log in with the admin credentials you created during installation. | |
7 | 7 | |
8 | 8 |  |
9 | 9 | |
10 | 10 | |
▼docs/getting-started/ssh-keys.md
+120-0
| @@ -0,0 +1,120 @@ | ||
1 | +--- | |
2 | +title: "SSH Keys & Deploy Keys" | |
3 | +description: "How to set up SSH keys for secure Git operations and deploy keys for CI/CD" | |
4 | +--- | |
5 | + | |
6 | +# SSH Keys & Deploy Keys | |
7 | + | |
8 | +OpenCodeHub supports SSH key authentication for secure Git operations without passwords. | |
9 | + | |
10 | +## SSH Keys (Account-Level) | |
11 | + | |
12 | +SSH keys give you access to **all your repositories**. Use them for daily development. | |
13 | + | |
14 | +### Generating an SSH Key | |
15 | + | |
16 | +**Ed25519 (recommended):** | |
17 | +```bash | |
18 | +ssh-keygen -t ed25519 -C "[email protected]" | |
19 | +``` | |
20 | + | |
21 | +**RSA (legacy):** | |
22 | +```bash | |
23 | +ssh-keygen -t rsa -b 4096 -C "[email protected]" | |
24 | +``` | |
25 | + | |
26 | +### Adding Your Key | |
27 | + | |
28 | +1. Copy your public key: | |
29 | + ```bash | |
30 | + cat ~/.ssh/id_ed25519.pub | |
31 | + ``` | |
32 | + | |
33 | +2. Go to **Settings → SSH Keys** in OpenCodeHub | |
34 | + | |
35 | +3. Click **New SSH Key** | |
36 | + | |
37 | +4. Paste your public key and give it a title (e.g., "Work Laptop") | |
38 | + | |
39 | +### Testing Your Connection | |
40 | + | |
41 | +```bash | |
42 | +ssh -T [email protected] | |
43 | +``` | |
44 | + | |
45 | +You should see: `Hi username! You've successfully authenticated` | |
46 | + | |
47 | +> **Note:** Replace `your-server.com` with your actual OpenCodeHub server hostname or IP address. | |
48 | + | |
49 | +### Using SSH with Git | |
50 | + | |
51 | +```bash | |
52 | +# Clone via SSH | |
53 | +git clone [email protected]:owner/repo.git | |
54 | + | |
55 | +# Add SSH remote to existing repo | |
56 | +git remote add origin [email protected]:owner/repo.git | |
57 | +``` | |
58 | + | |
59 | +## Deploy Keys (Repository-Level) | |
60 | + | |
61 | +Deploy keys grant access to a **single repository**. Use them for CI/CD and deployments. | |
62 | + | |
63 | +### When to Use Deploy Keys | |
64 | + | |
65 | +- CI/CD pipelines that need to pull code | |
66 | +- Deployment servers | |
67 | +- Automation scripts with limited access | |
68 | + | |
69 | +### Generating a Deploy Key | |
70 | + | |
71 | +```bash | |
72 | +ssh-keygen -t ed25519 -C "deploy@production-server" | |
73 | +``` | |
74 | + | |
75 | +### Adding a Deploy Key | |
76 | + | |
77 | +1. Go to **Repository → Settings → Deploy Keys** | |
78 | + | |
79 | +2. Click **Add deploy key** | |
80 | + | |
81 | +3. Paste the public key and give it a title | |
82 | + | |
83 | +4. Choose **Read-only** or **Read/write** access | |
84 | + | |
85 | +### Key Differences | |
86 | + | |
87 | +| Feature | SSH Keys | Deploy Keys | | |
88 | +|---------|----------|-------------| | |
89 | +| Scope | All repositories | Single repository | | |
90 | +| Access | Full account access | Read-only or read/write | | |
91 | +| Use case | Daily development | CI/CD, deployment | | |
92 | + | |
93 | +## Troubleshooting | |
94 | + | |
95 | +### Permission Denied | |
96 | + | |
97 | +```bash | |
98 | +# Check SSH agent | |
99 | +ssh-add -l | |
100 | + | |
101 | +# Add key to agent | |
102 | +ssh-add ~/.ssh/id_ed25519 | |
103 | + | |
104 | +# Debug SSH connection | |
105 | +ssh -vvv [email protected] | |
106 | +``` | |
107 | + | |
108 | +### Wrong Key Being Used | |
109 | + | |
110 | +```bash | |
111 | +# Specify key explicitly | |
112 | +ssh -i ~/.ssh/id_ed25519 [email protected] | |
113 | +``` | |
114 | + | |
115 | +### Host Key Verification Failed | |
116 | + | |
117 | +```bash | |
118 | +# Add host to known_hosts | |
119 | +ssh-keyscan your-server.com >> ~/.ssh/known_hosts | |
120 | +``` | |
0 | 121 | |
▼docs/reference/api-versioning.md
+14-0
| @@ -0,0 +1,14 @@ | ||
1 | +# API Versioning | |
2 | + | |
3 | +OpenCodeHub provides a stable REST API and GraphQL endpoint. | |
4 | + | |
5 | +## Versioning Strategy | |
6 | +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. | |
7 | + | |
8 | +When breaking changes are unavoidable: | |
9 | +- They will be announced in the release notes. | |
10 | +- A deprecation header (`Deprecation: <date>`) will be added to the response of the old endpoint. | |
11 | +- Clients will have at least one minor release cycle to migrate. | |
12 | + | |
13 | +## GraphQL | |
14 | +The GraphQL API inherently supports seamless evolution by deprecating specific fields using the `@deprecated` directive, allowing clients to transition without breaking version changes. | |
0 | 15 | |
▼drizzle/0000_abandoned_king_bedlam.sql
+0-36
| @@ -1,36 +0,0 @@ | ||
1 | -ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "ai_config" text; | |
2 | - | |
3 | -CREATE TABLE IF NOT EXISTS "organizations" ( | |
4 | - "id" text PRIMARY KEY NOT NULL, | |
5 | - "name" text NOT NULL, | |
6 | - "display_name" text, | |
7 | - "description" text, | |
8 | - "email" text, | |
9 | - "location" text, | |
10 | - "website" text, | |
11 | - "avatar_url" text, | |
12 | - "is_verified" boolean DEFAULT false, | |
13 | - "created_at" timestamp DEFAULT now() NOT NULL, | |
14 | - "updated_at" timestamp DEFAULT now() NOT NULL, | |
15 | - CONSTRAINT "organizations_name_unique" UNIQUE("name") | |
16 | -); | |
17 | - | |
18 | -CREATE TABLE IF NOT EXISTS "organization_members" ( | |
19 | - "organization_id" text NOT NULL, | |
20 | - "user_id" text NOT NULL, | |
21 | - "role" text DEFAULT 'member' NOT NULL, | |
22 | - "created_at" timestamp DEFAULT now() NOT NULL, | |
23 | - CONSTRAINT "organization_members_organization_id_user_id_pk" PRIMARY KEY("organization_id","user_id") | |
24 | -); | |
25 | - | |
26 | -DO $$ BEGIN | |
27 | - 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; | |
28 | -EXCEPTION | |
29 | - WHEN duplicate_object THEN null; | |
30 | -END $$; | |
31 | - | |
32 | -DO $$ BEGIN | |
33 | - 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; | |
34 | -EXCEPTION | |
35 | - WHEN duplicate_object THEN null; | |
36 | -END $$; | |
37 | 0 | |
▼drizzle/0000_black_monster_badoon.sql
+0-992
| @@ -1,1055 +0,0 @@ | ||
1 | -CREATE TABLE `email_verification_tokens` ( | |
2 | - `id` text PRIMARY KEY NOT NULL, | |
3 | - `user_id` text NOT NULL, | |
4 | - `token` text NOT NULL, | |
5 | - `expires_at` text NOT NULL, | |
6 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
7 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
8 | -); | |
9 | -CREATE UNIQUE INDEX `email_verification_tokens_token_unique` ON `email_verification_tokens` (`token`);--> statement-breakpoint | |
10 | -CREATE TABLE `gpg_keys` ( | |
11 | - `id` text PRIMARY KEY NOT NULL, | |
12 | - `user_id` text NOT NULL, | |
13 | - `name` text NOT NULL, | |
14 | - `key_id` text NOT NULL, | |
15 | - `public_key` text NOT NULL, | |
16 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
17 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
18 | -); | |
19 | -CREATE TABLE `oauth_accounts` ( | |
20 | - `id` text PRIMARY KEY NOT NULL, | |
21 | - `user_id` text NOT NULL, | |
22 | - `provider` text NOT NULL, | |
23 | - `provider_account_id` text NOT NULL, | |
24 | - `access_token` text, | |
25 | - `refresh_token` text, | |
26 | - `expires_at` text, | |
27 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
28 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
29 | -); | |
30 | -CREATE TABLE `password_reset_tokens` ( | |
31 | - `id` text PRIMARY KEY NOT NULL, | |
32 | - `user_id` text NOT NULL, | |
33 | - `token` text NOT NULL, | |
34 | - `expires_at` text NOT NULL, | |
35 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
36 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
37 | -); | |
38 | -CREATE UNIQUE INDEX `password_reset_tokens_token_unique` ON `password_reset_tokens` (`token`);--> statement-breakpoint | |
39 | -CREATE TABLE `personal_access_tokens` ( | |
40 | - `id` text PRIMARY KEY NOT NULL, | |
41 | - `user_id` text NOT NULL, | |
42 | - `name` text NOT NULL, | |
43 | - `token` text NOT NULL, | |
44 | - `expires_at` text, | |
45 | - `last_used_at` text, | |
46 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
47 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
48 | -); | |
49 | -CREATE UNIQUE INDEX `personal_access_tokens_token_unique` ON `personal_access_tokens` (`token`);--> statement-breakpoint | |
50 | -CREATE TABLE `sessions` ( | |
51 | - `id` text PRIMARY KEY NOT NULL, | |
52 | - `user_id` text NOT NULL, | |
53 | - `token` text NOT NULL, | |
54 | - `user_agent` text, | |
55 | - `ip_address` text, | |
56 | - `expires_at` text NOT NULL, | |
57 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
58 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
59 | -); | |
60 | -CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint | |
61 | -CREATE TABLE `ssh_keys` ( | |
62 | - `id` text PRIMARY KEY NOT NULL, | |
63 | - `user_id` text NOT NULL, | |
64 | - `title` text NOT NULL, | |
65 | - `fingerprint` text NOT NULL, | |
66 | - `public_key` text NOT NULL, | |
67 | - `key_type` text NOT NULL, | |
68 | - `last_used_at` text, | |
69 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
70 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
71 | -); | |
72 | -CREATE UNIQUE INDEX `ssh_keys_fingerprint_unique` ON `ssh_keys` (`fingerprint`);--> statement-breakpoint | |
73 | -CREATE TABLE `users` ( | |
74 | - `id` text PRIMARY KEY NOT NULL, | |
75 | - `username` text NOT NULL, | |
76 | - `email` text NOT NULL, | |
77 | - `password_hash` text, | |
78 | - `display_name` text, | |
79 | - `bio` text, | |
80 | - `avatar_url` text, | |
81 | - `location` text, | |
82 | - `website` text, | |
83 | - `company` text, | |
84 | - `is_admin` integer DEFAULT false, | |
85 | - `is_active` integer DEFAULT true, | |
86 | - `email_verified` integer DEFAULT false, | |
87 | - `two_factor_enabled` integer DEFAULT false, | |
88 | - `two_factor_secret` text, | |
89 | - `last_login_at` text, | |
90 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
91 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL | |
92 | -); | |
93 | -CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint | |
94 | -CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint | |
95 | -CREATE TABLE `branches` ( | |
96 | - `id` text PRIMARY KEY NOT NULL, | |
97 | - `repository_id` text NOT NULL, | |
98 | - `name` text NOT NULL, | |
99 | - `commit_sha` text NOT NULL, | |
100 | - `is_protected` integer DEFAULT false, | |
101 | - `is_default` integer DEFAULT false, | |
102 | - `protection_rules` text, | |
103 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
104 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
105 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
106 | -); | |
107 | -CREATE TABLE `commits` ( | |
108 | - `id` text PRIMARY KEY NOT NULL, | |
109 | - `repository_id` text NOT NULL, | |
110 | - `sha` text NOT NULL, | |
111 | - `message` text NOT NULL, | |
112 | - `author_name` text NOT NULL, | |
113 | - `author_email` text NOT NULL, | |
114 | - `author_date` text NOT NULL, | |
115 | - `committer_name` text NOT NULL, | |
116 | - `committer_email` text NOT NULL, | |
117 | - `committer_date` text NOT NULL, | |
118 | - `parent_shas` text, | |
119 | - `tree_sha` text, | |
120 | - `user_id` text, | |
121 | - `stats` text, | |
122 | - `signature` text, | |
123 | - `is_verified` integer DEFAULT false, | |
124 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
125 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
126 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
127 | -); | |
128 | -CREATE TABLE `releases` ( | |
129 | - `id` text PRIMARY KEY NOT NULL, | |
130 | - `repository_id` text NOT NULL, | |
131 | - `tag_id` text, | |
132 | - `name` text NOT NULL, | |
133 | - `body` text, | |
134 | - `is_draft` integer DEFAULT false, | |
135 | - `is_prerelease` integer DEFAULT false, | |
136 | - `author_id` text, | |
137 | - `assets` text, | |
138 | - `download_count` integer DEFAULT 0, | |
139 | - `published_at` text, | |
140 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
141 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
142 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
143 | - FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE no action, | |
144 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
145 | -); | |
146 | -CREATE TABLE `repositories` ( | |
147 | - `id` text PRIMARY KEY NOT NULL, | |
148 | - `name` text NOT NULL, | |
149 | - `slug` text NOT NULL, | |
150 | - `description` text, | |
151 | - `owner_id` text NOT NULL, | |
152 | - `owner_type` text DEFAULT 'user' NOT NULL, | |
153 | - `visibility` text DEFAULT 'public' NOT NULL, | |
154 | - `default_branch` text DEFAULT 'main' NOT NULL, | |
155 | - `disk_path` text NOT NULL, | |
156 | - `ssh_clone_url` text, | |
157 | - `http_clone_url` text, | |
158 | - `star_count` integer DEFAULT 0, | |
159 | - `fork_count` integer DEFAULT 0, | |
160 | - `watch_count` integer DEFAULT 0, | |
161 | - `open_issue_count` integer DEFAULT 0, | |
162 | - `open_pr_count` integer DEFAULT 0, | |
163 | - `size` integer DEFAULT 0, | |
164 | - `is_fork` integer DEFAULT false, | |
165 | - `forked_from_id` text, | |
166 | - `is_archived` integer DEFAULT false, | |
167 | - `is_mirror` integer DEFAULT false, | |
168 | - `mirror_url` text, | |
169 | - `has_issues` integer DEFAULT true, | |
170 | - `has_wiki` integer DEFAULT true, | |
171 | - `has_actions` integer DEFAULT true, | |
172 | - `allow_forking` integer DEFAULT true, | |
173 | - `license_type` text, | |
174 | - `topics` text, | |
175 | - `language` text, | |
176 | - `languages` text, | |
177 | - `last_activity_at` text, | |
178 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
179 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
180 | - FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
181 | - FOREIGN KEY (`forked_from_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action | |
182 | -); | |
183 | -CREATE TABLE `repository_collaborators` ( | |
184 | - `id` text PRIMARY KEY NOT NULL, | |
185 | - `repository_id` text NOT NULL, | |
186 | - `user_id` text NOT NULL, | |
187 | - `role` text DEFAULT 'developer' NOT NULL, | |
188 | - `added_by_id` text, | |
189 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
190 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
191 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
192 | - FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
193 | -); | |
194 | -CREATE TABLE `repository_stars` ( | |
195 | - `id` text PRIMARY KEY NOT NULL, | |
196 | - `repository_id` text NOT NULL, | |
197 | - `user_id` text NOT NULL, | |
198 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
199 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
200 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
201 | -); | |
202 | -CREATE TABLE `repository_watchers` ( | |
203 | - `id` text PRIMARY KEY NOT NULL, | |
204 | - `repository_id` text NOT NULL, | |
205 | - `user_id` text NOT NULL, | |
206 | - `watch_level` text DEFAULT 'watching' NOT NULL, | |
207 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
208 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
209 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
210 | -); | |
211 | -CREATE TABLE `tags` ( | |
212 | - `id` text PRIMARY KEY NOT NULL, | |
213 | - `repository_id` text NOT NULL, | |
214 | - `name` text NOT NULL, | |
215 | - `commit_sha` text NOT NULL, | |
216 | - `message` text, | |
217 | - `tagger_name` text, | |
218 | - `tagger_email` text, | |
219 | - `tagged_at` text, | |
220 | - `is_release` integer DEFAULT false, | |
221 | - `release_id` text, | |
222 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
223 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
224 | -); | |
225 | -CREATE TABLE `webhooks` ( | |
226 | - `id` text PRIMARY KEY NOT NULL, | |
227 | - `repository_id` text NOT NULL, | |
228 | - `url` text NOT NULL, | |
229 | - `secret` text, | |
230 | - `events` text NOT NULL, | |
231 | - `is_active` integer DEFAULT true, | |
232 | - `last_delivery_at` text, | |
233 | - `last_delivery_status` integer, | |
234 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
235 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
236 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
237 | -); | |
238 | -CREATE TABLE `issue_assignees` ( | |
239 | - `id` text PRIMARY KEY NOT NULL, | |
240 | - `issue_id` text NOT NULL, | |
241 | - `user_id` text NOT NULL, | |
242 | - `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
243 | - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
244 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
245 | -); | |
246 | -CREATE TABLE `issue_comments` ( | |
247 | - `id` text PRIMARY KEY NOT NULL, | |
248 | - `issue_id` text NOT NULL, | |
249 | - `author_id` text NOT NULL, | |
250 | - `body` text NOT NULL, | |
251 | - `reactions` text, | |
252 | - `is_edited` integer DEFAULT false, | |
253 | - `edited_at` text, | |
254 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
255 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
256 | - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
257 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
258 | -); | |
259 | -CREATE TABLE `issue_labels` ( | |
260 | - `id` text PRIMARY KEY NOT NULL, | |
261 | - `issue_id` text NOT NULL, | |
262 | - `label_id` text NOT NULL, | |
263 | - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
264 | - FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade | |
265 | -); | |
266 | -CREATE TABLE `issue_subscribers` ( | |
267 | - `id` text PRIMARY KEY NOT NULL, | |
268 | - `issue_id` text NOT NULL, | |
269 | - `user_id` text NOT NULL, | |
270 | - `subscribed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
271 | - FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
272 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
273 | -); | |
274 | -CREATE TABLE `issues` ( | |
275 | - `id` text PRIMARY KEY NOT NULL, | |
276 | - `repository_id` text NOT NULL, | |
277 | - `number` integer NOT NULL, | |
278 | - `title` text NOT NULL, | |
279 | - `body` text, | |
280 | - `state` text DEFAULT 'open' NOT NULL, | |
281 | - `author_id` text NOT NULL, | |
282 | - `assignee_id` text, | |
283 | - `milestone_id` text, | |
284 | - `is_pinned` integer DEFAULT false, | |
285 | - `is_locked` integer DEFAULT false, | |
286 | - `lock_reason` text, | |
287 | - `comment_count` integer DEFAULT 0, | |
288 | - `reactions` text, | |
289 | - `closed_at` text, | |
290 | - `closed_by_id` text, | |
291 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
292 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
293 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
294 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
295 | - FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
296 | - FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action, | |
297 | - FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
298 | -); | |
299 | -CREATE TABLE `labels` ( | |
300 | - `id` text PRIMARY KEY NOT NULL, | |
301 | - `repository_id` text NOT NULL, | |
302 | - `name` text NOT NULL, | |
303 | - `color` text DEFAULT '#6b7280' NOT NULL, | |
304 | - `description` text, | |
305 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
306 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
307 | -); | |
308 | -CREATE TABLE `milestones` ( | |
309 | - `id` text PRIMARY KEY NOT NULL, | |
310 | - `repository_id` text NOT NULL, | |
311 | - `title` text NOT NULL, | |
312 | - `description` text, | |
313 | - `state` text DEFAULT 'open' NOT NULL, | |
314 | - `due_date` text, | |
315 | - `open_issue_count` integer DEFAULT 0, | |
316 | - `closed_issue_count` integer DEFAULT 0, | |
317 | - `closed_at` text, | |
318 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
319 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
320 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
321 | -); | |
322 | -CREATE TABLE `pull_request_assignees` ( | |
323 | - `id` text PRIMARY KEY NOT NULL, | |
324 | - `pull_request_id` text NOT NULL, | |
325 | - `user_id` text NOT NULL, | |
326 | - `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
327 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
328 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
329 | -); | |
330 | -CREATE TABLE `pull_request_checks` ( | |
331 | - `id` text PRIMARY KEY NOT NULL, | |
332 | - `pull_request_id` text NOT NULL, | |
333 | - `name` text NOT NULL, | |
334 | - `status` text NOT NULL, | |
335 | - `conclusion` text, | |
336 | - `head_sha` text NOT NULL, | |
337 | - `external_id` text, | |
338 | - `details_url` text, | |
339 | - `output` text, | |
340 | - `started_at` text, | |
341 | - `completed_at` text, | |
342 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
343 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
344 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade | |
345 | -); | |
346 | -CREATE TABLE `pull_request_comments` ( | |
347 | - `id` text PRIMARY KEY NOT NULL, | |
348 | - `pull_request_id` text NOT NULL, | |
349 | - `review_id` text, | |
350 | - `author_id` text NOT NULL, | |
351 | - `body` text NOT NULL, | |
352 | - `path` text, | |
353 | - `line` integer, | |
354 | - `side` text, | |
355 | - `start_line` integer, | |
356 | - `start_side` text, | |
357 | - `commit_sha` text, | |
358 | - `original_commit_sha` text, | |
359 | - `original_line` integer, | |
360 | - `in_reply_to_id` text, | |
361 | - `reactions` text, | |
362 | - `is_resolved` integer DEFAULT false, | |
363 | - `resolved_by_id` text, | |
364 | - `resolved_at` text, | |
365 | - `is_edited` integer DEFAULT false, | |
366 | - `edited_at` text, | |
367 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
368 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
369 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
370 | - FOREIGN KEY (`review_id`) REFERENCES `pull_request_reviews`(`id`) ON UPDATE no action ON DELETE no action, | |
371 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
372 | - FOREIGN KEY (`in_reply_to_id`) REFERENCES `pull_request_comments`(`id`) ON UPDATE no action ON DELETE no action, | |
373 | - FOREIGN KEY (`resolved_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
374 | -); | |
375 | -CREATE TABLE `pull_request_labels` ( | |
376 | - `id` text PRIMARY KEY NOT NULL, | |
377 | - `pull_request_id` text NOT NULL, | |
378 | - `label_id` text NOT NULL, | |
379 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
380 | - FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade | |
381 | -); | |
382 | -CREATE TABLE `pull_request_reviewers` ( | |
383 | - `id` text PRIMARY KEY NOT NULL, | |
384 | - `pull_request_id` text NOT NULL, | |
385 | - `user_id` text NOT NULL, | |
386 | - `is_required` integer DEFAULT false, | |
387 | - `requested_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
388 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
389 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
390 | -); | |
391 | -CREATE TABLE `pull_request_reviews` ( | |
392 | - `id` text PRIMARY KEY NOT NULL, | |
393 | - `pull_request_id` text NOT NULL, | |
394 | - `reviewer_id` text NOT NULL, | |
395 | - `state` text NOT NULL, | |
396 | - `body` text, | |
397 | - `commit_sha` text, | |
398 | - `submitted_at` text, | |
399 | - `dismissed_at` text, | |
400 | - `dismissed_by_id` text, | |
401 | - `dismissal_reason` text, | |
402 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
403 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
404 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
405 | - FOREIGN KEY (`reviewer_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
406 | - FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
407 | -); | |
408 | -CREATE TABLE `pull_requests` ( | |
409 | - `id` text PRIMARY KEY NOT NULL, | |
410 | - `repository_id` text NOT NULL, | |
411 | - `number` integer NOT NULL, | |
412 | - `title` text NOT NULL, | |
413 | - `body` text, | |
414 | - `state` text DEFAULT 'open' NOT NULL, | |
415 | - `author_id` text NOT NULL, | |
416 | - `assignee_id` text, | |
417 | - `milestone_id` text, | |
418 | - `head_branch` text NOT NULL, | |
419 | - `head_sha` text NOT NULL, | |
420 | - `head_repository_id` text, | |
421 | - `base_branch` text NOT NULL, | |
422 | - `base_sha` text NOT NULL, | |
423 | - `is_draft` integer DEFAULT false, | |
424 | - `is_merged` integer DEFAULT false, | |
425 | - `merged_at` text, | |
426 | - `merged_by_id` text, | |
427 | - `merge_sha` text, | |
428 | - `merge_method` text, | |
429 | - `additions` integer DEFAULT 0, | |
430 | - `deletions` integer DEFAULT 0, | |
431 | - `changed_files` integer DEFAULT 0, | |
432 | - `comment_count` integer DEFAULT 0, | |
433 | - `review_count` integer DEFAULT 0, | |
434 | - `mergeable` integer, | |
435 | - `mergeable_state` text, | |
436 | - `rebaseable` integer, | |
437 | - `maintainer_can_modify` integer DEFAULT true, | |
438 | - `allow_auto_merge` integer DEFAULT false, | |
439 | - `auto_merge_method` text, | |
440 | - `closed_at` text, | |
441 | - `closed_by_id` text, | |
442 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
443 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
444 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
445 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
446 | - FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
447 | - FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action, | |
448 | - FOREIGN KEY (`head_repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action, | |
449 | - FOREIGN KEY (`merged_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
450 | - FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
451 | -); | |
452 | -CREATE TABLE `scheduled_workflows` ( | |
453 | - `id` text PRIMARY KEY NOT NULL, | |
454 | - `workflow_id` text NOT NULL, | |
455 | - `cron_expression` text NOT NULL, | |
456 | - `timezone` text DEFAULT 'UTC', | |
457 | - `is_enabled` integer DEFAULT true, | |
458 | - `last_run_at` text, | |
459 | - `next_run_at` text, | |
460 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
461 | - FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade | |
462 | -); | |
463 | -CREATE TABLE `workflow_artifacts` ( | |
464 | - `id` text PRIMARY KEY NOT NULL, | |
465 | - `run_id` text NOT NULL, | |
466 | - `job_id` text, | |
467 | - `name` text NOT NULL, | |
468 | - `size_bytes` integer NOT NULL, | |
469 | - `storage_path` text NOT NULL, | |
470 | - `mime_type` text, | |
471 | - `download_count` integer DEFAULT 0, | |
472 | - `expires_at` text, | |
473 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
474 | - FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade, | |
475 | - FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE no action | |
476 | -); | |
477 | -CREATE TABLE `workflow_jobs` ( | |
478 | - `id` text PRIMARY KEY NOT NULL, | |
479 | - `run_id` text NOT NULL, | |
480 | - `name` text NOT NULL, | |
481 | - `status` text DEFAULT 'queued' NOT NULL, | |
482 | - `conclusion` text, | |
483 | - `runner_id` text, | |
484 | - `runner_name` text, | |
485 | - `runner_group_id` text, | |
486 | - `runner_group_name` text, | |
487 | - `container_id` text, | |
488 | - `container_image` text, | |
489 | - `needs` text, | |
490 | - `environment` text, | |
491 | - `environment_url` text, | |
492 | - `matrix` text, | |
493 | - `started_at` text, | |
494 | - `completed_at` text, | |
495 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
496 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
497 | - FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade | |
498 | -); | |
499 | -CREATE TABLE `workflow_logs` ( | |
500 | - `id` text PRIMARY KEY NOT NULL, | |
501 | - `job_id` text NOT NULL, | |
502 | - `step_id` text, | |
503 | - `log_level` text DEFAULT 'info' NOT NULL, | |
504 | - `message` text NOT NULL, | |
505 | - `timestamp` text NOT NULL, | |
506 | - `line_number` integer, | |
507 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
508 | - FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade, | |
509 | - FOREIGN KEY (`step_id`) REFERENCES `workflow_steps`(`id`) ON UPDATE no action ON DELETE no action | |
510 | -); | |
511 | -CREATE TABLE `workflow_runs` ( | |
512 | - `id` text PRIMARY KEY NOT NULL, | |
513 | - `workflow_id` text NOT NULL, | |
514 | - `repository_id` text NOT NULL, | |
515 | - `run_number` integer NOT NULL, | |
516 | - `run_attempt` integer DEFAULT 1 NOT NULL, | |
517 | - `name` text NOT NULL, | |
518 | - `display_title` text, | |
519 | - `status` text DEFAULT 'queued' NOT NULL, | |
520 | - `conclusion` text, | |
521 | - `event` text NOT NULL, | |
522 | - `head_branch` text, | |
523 | - `head_sha` text NOT NULL, | |
524 | - `base_branch` text, | |
525 | - `base_sha` text, | |
526 | - `pull_request_id` text, | |
527 | - `triggered_by_id` text, | |
528 | - `started_at` text, | |
529 | - `completed_at` text, | |
530 | - `workflow_config` text, | |
531 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
532 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
533 | - FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade, | |
534 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
535 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action, | |
536 | - FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
537 | -); | |
538 | -CREATE TABLE `workflow_secrets` ( | |
539 | - `id` text PRIMARY KEY NOT NULL, | |
540 | - `repository_id` text NOT NULL, | |
541 | - `name` text NOT NULL, | |
542 | - `encrypted_value` text NOT NULL, | |
543 | - `environment` text, | |
544 | - `created_by_id` text, | |
545 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
546 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
547 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
548 | - FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
549 | -); | |
550 | -CREATE TABLE `workflow_steps` ( | |
551 | - `id` text PRIMARY KEY NOT NULL, | |
552 | - `job_id` text NOT NULL, | |
553 | - `number` integer NOT NULL, | |
554 | - `name` text NOT NULL, | |
555 | - `status` text DEFAULT 'queued' NOT NULL, | |
556 | - `conclusion` text, | |
557 | - `uses` text, | |
558 | - `run` text, | |
559 | - `shell` text, | |
560 | - `working_directory` text, | |
561 | - `env` text, | |
562 | - `with` text, | |
563 | - `if` text, | |
564 | - `continue_on_error` integer DEFAULT false, | |
565 | - `timeout_minutes` integer, | |
566 | - `started_at` text, | |
567 | - `completed_at` text, | |
568 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
569 | - FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade | |
570 | -); | |
571 | -CREATE TABLE `workflow_variables` ( | |
572 | - `id` text PRIMARY KEY NOT NULL, | |
573 | - `repository_id` text NOT NULL, | |
574 | - `name` text NOT NULL, | |
575 | - `value` text NOT NULL, | |
576 | - `environment` text, | |
577 | - `created_by_id` text, | |
578 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
579 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
580 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
581 | - FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
582 | -); | |
583 | -CREATE TABLE `workflows` ( | |
584 | - `id` text PRIMARY KEY NOT NULL, | |
585 | - `repository_id` text NOT NULL, | |
586 | - `name` text NOT NULL, | |
587 | - `path` text NOT NULL, | |
588 | - `state` text DEFAULT 'active' NOT NULL, | |
589 | - `badge_url` text, | |
590 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
591 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
592 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
593 | -); | |
594 | -CREATE TABLE `wiki_pages` ( | |
595 | - `id` text PRIMARY KEY NOT NULL, | |
596 | - `repository_id` text NOT NULL, | |
597 | - `slug` text NOT NULL, | |
598 | - `title` text NOT NULL, | |
599 | - `content` text NOT NULL, | |
600 | - `format` text DEFAULT 'markdown' NOT NULL, | |
601 | - `parent_id` text, | |
602 | - `order` integer DEFAULT 0, | |
603 | - `last_editor_id` text, | |
604 | - `view_count` integer DEFAULT 0, | |
605 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
606 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
607 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
608 | - FOREIGN KEY (`parent_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE no action, | |
609 | - FOREIGN KEY (`last_editor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
610 | -); | |
611 | -CREATE TABLE `wiki_revisions` ( | |
612 | - `id` text PRIMARY KEY NOT NULL, | |
613 | - `page_id` text NOT NULL, | |
614 | - `content` text NOT NULL, | |
615 | - `message` text, | |
616 | - `author_id` text NOT NULL, | |
617 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
618 | - FOREIGN KEY (`page_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE cascade, | |
619 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
620 | -); | |
621 | -CREATE TABLE `organization_members` ( | |
622 | - `id` text PRIMARY KEY NOT NULL, | |
623 | - `organization_id` text NOT NULL, | |
624 | - `user_id` text NOT NULL, | |
625 | - `role` text DEFAULT 'member' NOT NULL, | |
626 | - `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
627 | - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, | |
628 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
629 | -); | |
630 | -CREATE TABLE `organizations` ( | |
631 | - `id` text PRIMARY KEY NOT NULL, | |
632 | - `name` text NOT NULL, | |
633 | - `display_name` text, | |
634 | - `description` text, | |
635 | - `avatar_url` text, | |
636 | - `website` text, | |
637 | - `location` text, | |
638 | - `email` text, | |
639 | - `is_verified` integer DEFAULT false, | |
640 | - `visibility` text DEFAULT 'public' NOT NULL, | |
641 | - `default_repo_permission` text DEFAULT 'read', | |
642 | - `members_can_create_repos` integer DEFAULT true, | |
643 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
644 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL | |
645 | -); | |
646 | -CREATE UNIQUE INDEX `organizations_name_unique` ON `organizations` (`name`);--> statement-breakpoint | |
647 | -CREATE TABLE `team_members` ( | |
648 | - `id` text PRIMARY KEY NOT NULL, | |
649 | - `team_id` text NOT NULL, | |
650 | - `user_id` text NOT NULL, | |
651 | - `role` text DEFAULT 'member' NOT NULL, | |
652 | - `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
653 | - FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, | |
654 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
655 | -); | |
656 | -CREATE TABLE `team_repositories` ( | |
657 | - `id` text PRIMARY KEY NOT NULL, | |
658 | - `team_id` text NOT NULL, | |
659 | - `repository_id` text NOT NULL, | |
660 | - `permission` text DEFAULT 'read' NOT NULL, | |
661 | - `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
662 | - FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, | |
663 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
664 | -); | |
665 | -CREATE TABLE `teams` ( | |
666 | - `id` text PRIMARY KEY NOT NULL, | |
667 | - `organization_id` text NOT NULL, | |
668 | - `name` text NOT NULL, | |
669 | - `slug` text NOT NULL, | |
670 | - `description` text, | |
671 | - `privacy` text DEFAULT 'visible' NOT NULL, | |
672 | - `parent_id` text, | |
673 | - `permission` text DEFAULT 'read' NOT NULL, | |
674 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
675 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
676 | - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, | |
677 | - FOREIGN KEY (`parent_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE no action | |
678 | -); | |
679 | -CREATE TABLE `activities` ( | |
680 | - `id` text PRIMARY KEY NOT NULL, | |
681 | - `user_id` text NOT NULL, | |
682 | - `repository_id` text, | |
683 | - `organization_id` text, | |
684 | - `type` text NOT NULL, | |
685 | - `action` text NOT NULL, | |
686 | - `ref_type` text, | |
687 | - `ref_name` text, | |
688 | - `target_type` text, | |
689 | - `target_id` text, | |
690 | - `payload` text, | |
691 | - `is_public` integer DEFAULT true, | |
692 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
693 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
694 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
695 | - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action | |
696 | -); | |
697 | -CREATE TABLE `audit_logs` ( | |
698 | - `id` text PRIMARY KEY NOT NULL, | |
699 | - `user_id` text, | |
700 | - `organization_id` text, | |
701 | - `repository_id` text, | |
702 | - `action` text NOT NULL, | |
703 | - `actor_type` text DEFAULT 'user' NOT NULL, | |
704 | - `actor_id` text, | |
705 | - `actor_ip` text, | |
706 | - `actor_user_agent` text, | |
707 | - `target_type` text, | |
708 | - `target_id` text, | |
709 | - `target_name` text, | |
710 | - `data` text, | |
711 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
712 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
713 | - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action, | |
714 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action | |
715 | -); | |
716 | -CREATE TABLE `notifications` ( | |
717 | - `id` text PRIMARY KEY NOT NULL, | |
718 | - `user_id` text NOT NULL, | |
719 | - `repository_id` text, | |
720 | - `type` text NOT NULL, | |
721 | - `title` text NOT NULL, | |
722 | - `body` text, | |
723 | - `url` text, | |
724 | - `actor_id` text, | |
725 | - `subject_type` text, | |
726 | - `subject_id` text, | |
727 | - `reason` text NOT NULL, | |
728 | - `is_read` integer DEFAULT false, | |
729 | - `read_at` text, | |
730 | - `is_archived` integer DEFAULT false, | |
731 | - `archived_at` text, | |
732 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
733 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
734 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
735 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action, | |
736 | - FOREIGN KEY (`actor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
737 | -); | |
738 | -CREATE TABLE `security_scans` ( | |
739 | - `id` text PRIMARY KEY NOT NULL, | |
740 | - `repository_id` text NOT NULL, | |
741 | - `status` text DEFAULT 'queued' NOT NULL, | |
742 | - `started_at` integer, | |
743 | - `completed_at` integer, | |
744 | - `critical_count` integer DEFAULT 0, | |
745 | - `high_count` integer DEFAULT 0, | |
746 | - `medium_count` integer DEFAULT 0, | |
747 | - `low_count` integer DEFAULT 0, | |
748 | - `unknown_count` integer DEFAULT 0, | |
749 | - `logs` text, | |
750 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
751 | -); | |
752 | -CREATE TABLE `security_vulnerabilities` ( | |
753 | - `id` text PRIMARY KEY NOT NULL, | |
754 | - `scan_id` text NOT NULL, | |
755 | - `vulnerability_id` text NOT NULL, | |
756 | - `pkg_name` text NOT NULL, | |
757 | - `installed_version` text, | |
758 | - `fixed_version` text, | |
759 | - `severity` text NOT NULL, | |
760 | - `title` text, | |
761 | - `description` text, | |
762 | - `target` text, | |
763 | - `class` text, | |
764 | - FOREIGN KEY (`scan_id`) REFERENCES `security_scans`(`id`) ON UPDATE no action ON DELETE cascade | |
765 | -); | |
766 | -CREATE TABLE `pipeline_runners` ( | |
767 | - `id` text PRIMARY KEY NOT NULL, | |
768 | - `repository_id` text, | |
769 | - `token` text NOT NULL, | |
770 | - `name` text NOT NULL, | |
771 | - `os` text, | |
772 | - `arch` text, | |
773 | - `version` text, | |
774 | - `status` text DEFAULT 'offline' NOT NULL, | |
775 | - `last_seen_at` text, | |
776 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
777 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
778 | -); | |
779 | -CREATE TABLE `pr_stack_entries` ( | |
780 | - `id` text PRIMARY KEY NOT NULL, | |
781 | - `stack_id` text NOT NULL, | |
782 | - `pull_request_id` text NOT NULL, | |
783 | - `stack_order` integer NOT NULL, | |
784 | - `parent_pr_id` text, | |
785 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
786 | - FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE cascade, | |
787 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
788 | - FOREIGN KEY (`parent_pr_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action | |
789 | -); | |
790 | -CREATE TABLE `pr_stacks` ( | |
791 | - `id` text PRIMARY KEY NOT NULL, | |
792 | - `repository_id` text NOT NULL, | |
793 | - `name` text, | |
794 | - `base_branch` text NOT NULL, | |
795 | - `status` text DEFAULT 'active' NOT NULL, | |
796 | - `created_by_id` text NOT NULL, | |
797 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
798 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
799 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
800 | - FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
801 | -); | |
802 | -CREATE TABLE `merge_queue` ( | |
803 | - `id` text PRIMARY KEY NOT NULL, | |
804 | - `repository_id` text NOT NULL, | |
805 | - `pull_request_id` text NOT NULL, | |
806 | - `stack_id` text, | |
807 | - `status` text DEFAULT 'pending' NOT NULL, | |
808 | - `priority` integer DEFAULT 0 NOT NULL, | |
809 | - `position` integer, | |
810 | - `ci_status` text DEFAULT 'pending', | |
811 | - `ci_run_id` text, | |
812 | - `added_by_id` text NOT NULL, | |
813 | - `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
814 | - `estimated_merge_at` text, | |
815 | - `started_at` text, | |
816 | - `completed_at` text, | |
817 | - `failure_reason` text, | |
818 | - `retry_count` integer DEFAULT 0, | |
819 | - `merge_method` text DEFAULT 'merge', | |
820 | - `delete_on_merge` integer DEFAULT true, | |
821 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
822 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
823 | - FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE no action, | |
824 | - FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
825 | -); | |
826 | -CREATE TABLE `ai_review_suggestions` ( | |
827 | - `id` text PRIMARY KEY NOT NULL, | |
828 | - `ai_review_id` text NOT NULL, | |
829 | - `path` text NOT NULL, | |
830 | - `line` integer, | |
831 | - `end_line` integer, | |
832 | - `severity` text NOT NULL, | |
833 | - `type` text NOT NULL, | |
834 | - `category` text, | |
835 | - `title` text NOT NULL, | |
836 | - `message` text NOT NULL, | |
837 | - `suggested_fix` text, | |
838 | - `explanation` text, | |
839 | - `is_applied` integer DEFAULT false, | |
840 | - `is_dismissed` integer DEFAULT false, | |
841 | - `applied_at` text, | |
842 | - `applied_by_id` text, | |
843 | - `dismissed_at` text, | |
844 | - `dismissed_by_id` text, | |
845 | - `dismiss_reason` text, | |
846 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
847 | - FOREIGN KEY (`ai_review_id`) REFERENCES `ai_reviews`(`id`) ON UPDATE no action ON DELETE cascade, | |
848 | - FOREIGN KEY (`applied_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
849 | - FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
850 | -); | |
851 | -CREATE TABLE `ai_reviews` ( | |
852 | - `id` text PRIMARY KEY NOT NULL, | |
853 | - `pull_request_id` text NOT NULL, | |
854 | - `status` text DEFAULT 'pending' NOT NULL, | |
855 | - `model` text NOT NULL, | |
856 | - `provider` text NOT NULL, | |
857 | - `stack_context` text, | |
858 | - `includes_stack_context` integer DEFAULT false, | |
859 | - `summary` text, | |
860 | - `overall_severity` text, | |
861 | - `suggestions_count` integer DEFAULT 0, | |
862 | - `tokens_used` integer, | |
863 | - `prompt_tokens` integer, | |
864 | - `completion_tokens` integer, | |
865 | - `cost_cents` integer, | |
866 | - `raw_response` text, | |
867 | - `triggered_by_id` text, | |
868 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
869 | - `started_at` text, | |
870 | - `completed_at` text, | |
871 | - `error_message` text, | |
872 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
873 | - FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
874 | -); | |
875 | -CREATE TABLE `pr_metrics` ( | |
876 | - `id` text PRIMARY KEY NOT NULL, | |
877 | - `pull_request_id` text NOT NULL, | |
878 | - `repository_id` text NOT NULL, | |
879 | - `author_id` text NOT NULL, | |
880 | - `time_to_first_review` integer, | |
881 | - `time_to_approval` integer, | |
882 | - `time_to_merge` integer, | |
883 | - `total_cycle_time` integer, | |
884 | - `review_rounds` integer DEFAULT 1, | |
885 | - `reviewers_count` integer DEFAULT 0, | |
886 | - `comments_count` integer DEFAULT 0, | |
887 | - `changes_requested_count` integer DEFAULT 0, | |
888 | - `lines_added` integer DEFAULT 0, | |
889 | - `lines_removed` integer DEFAULT 0, | |
890 | - `files_changed` integer DEFAULT 0, | |
891 | - `commits` integer DEFAULT 1, | |
892 | - `is_stacked` integer DEFAULT false, | |
893 | - `stack_position` integer, | |
894 | - `pr_created_at` text, | |
895 | - `first_review_at` text, | |
896 | - `approved_at` text, | |
897 | - `merged_at` text, | |
898 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
899 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
900 | - FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
901 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
902 | - FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
903 | -); | |
904 | -CREATE TABLE `repo_metrics` ( | |
905 | - `id` text PRIMARY KEY NOT NULL, | |
906 | - `repository_id` text NOT NULL, | |
907 | - `week_of` text NOT NULL, | |
908 | - `prs_opened` integer DEFAULT 0, | |
909 | - `prs_merged` integer DEFAULT 0, | |
910 | - `prs_closed` integer DEFAULT 0, | |
911 | - `avg_time_to_first_review` integer, | |
912 | - `avg_time_to_merge` integer, | |
913 | - `avg_review_rounds` integer, | |
914 | - `avg_lines_changed` integer, | |
915 | - `avg_files_changed` integer, | |
916 | - `stacked_prs` integer DEFAULT 0, | |
917 | - `stacked_prs_percentage` integer DEFAULT 0, | |
918 | - `active_authors` integer DEFAULT 0, | |
919 | - `active_reviewers` integer DEFAULT 0, | |
920 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
921 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
922 | -); | |
923 | -CREATE TABLE `review_metrics` ( | |
924 | - `id` text PRIMARY KEY NOT NULL, | |
925 | - `user_id` text NOT NULL, | |
926 | - `repository_id` text, | |
927 | - `organization_id` text, | |
928 | - `week_of` text NOT NULL, | |
929 | - `prs_authored` integer DEFAULT 0, | |
930 | - `prs_authored_merged` integer DEFAULT 0, | |
931 | - `avg_time_to_merge_authored` integer, | |
932 | - `lines_authored_added` integer DEFAULT 0, | |
933 | - `lines_authored_removed` integer DEFAULT 0, | |
934 | - `prs_reviewed` integer DEFAULT 0, | |
935 | - `avg_time_to_review` integer, | |
936 | - `comments_given` integer DEFAULT 0, | |
937 | - `approvals_given` integer DEFAULT 0, | |
938 | - `changes_requested_given` integer DEFAULT 0, | |
939 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
940 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
941 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
942 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
943 | - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action | |
944 | -); | |
945 | -CREATE TABLE `slack_channel_mappings` ( | |
946 | - `id` text PRIMARY KEY NOT NULL, | |
947 | - `workspace_id` text NOT NULL, | |
948 | - `repository_id` text NOT NULL, | |
949 | - `channel_id` text NOT NULL, | |
950 | - `channel_name` text, | |
951 | - `notify_on` text DEFAULT '["pr_created","pr_merged","review_requested","ci_failed"]', | |
952 | - `notify_branches` text, | |
953 | - `notify_authors` text, | |
954 | - `is_active` integer DEFAULT true, | |
955 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
956 | - `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
957 | - FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade, | |
958 | - FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
959 | -); | |
960 | -CREATE TABLE `slack_user_mappings` ( | |
961 | - `id` text PRIMARY KEY NOT NULL, | |
962 | - `user_id` text NOT NULL, | |
963 | - `workspace_id` text NOT NULL, | |
964 | - `slack_user_id` text NOT NULL, | |
965 | - `slack_username` text, | |
966 | - `dm_preferences` text DEFAULT '{"review_requested":true,"pr_approved":true,"ci_failed":true}', | |
967 | - `dnd_enabled` integer DEFAULT false, | |
968 | - `dnd_start` text, | |
969 | - `dnd_end` text, | |
970 | - `dnd_timezone` text, | |
971 | - `is_active` integer DEFAULT true, | |
972 | - `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
973 | - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
974 | - FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade | |
975 | -); | |
976 | -CREATE TABLE `slack_workspaces` ( | |
977 | - `id` text PRIMARY KEY NOT NULL, | |
978 | - `organization_id` text, | |
979 | - `team_id` text NOT NULL, | |
980 | - `team_name` text, | |
981 | - `team_domain` text, | |
982 | - `access_token` text NOT NULL, | |
983 | - `bot_user_id` text, | |
984 | - `bot_access_token` text, | |
985 | - `scopes` text, | |
986 | - `installed_by_id` text NOT NULL, | |
987 | - `installed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
988 | - `is_active` integer DEFAULT true, | |
989 | - `last_used_at` text, | |
990 | - FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, | |
991 | - FOREIGN KEY (`installed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
992 | -); | |
993 | 0 | |