diff --git a/README.md b/README.md index 65ac7d332..70181581f 100644 --- a/README.md +++ b/README.md @@ -335,6 +335,23 @@ cd ../gitnexus-web && npm install npm run dev ``` +## Docker + +```bash +docker run --rm \ + --name gitnexus-web \ + -p 8080:80 \ + ghcr.io/abhigyanpatwari/gitnexus-web:latest +``` + +Or with Docker Compose: + +```bash +docker compose -f docker/compose.yaml up -d +``` + +Docker details, including the example `.env`, compose file, and the source [docker/Dockerfile](docker/Dockerfile), live in [docker/README.md](docker/README.md). + The web UI uses the same indexing pipeline as the CLI but runs entirely in WebAssembly (Tree-sitter WASM, LadybugDB WASM, in-browser embeddings). It's great for quick exploration but limited by browser memory for larger repos. **Local Backend Mode:** Run `gitnexus serve` and open the web UI locally — it auto-detects the server and shows all your indexed repos, with full AI chat support. No need to re-upload or re-index. The agent's tools (Cypher queries, search, code navigation) route through the backend HTTP API automatically. diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 000000000..9eb399fc1 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,3 @@ +IMAGE_NAME=ghcr.io/abhigyanpatwari/gitnexus-web:latest +CONTAINER_NAME=gitnexus-web +HOST_PORT=8080 diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..6ab998124 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,25 @@ +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY gitnexus-shared/package.json gitnexus-shared/package-lock.json ./gitnexus-shared/ +RUN npm ci --prefix gitnexus-shared + +COPY gitnexus-shared ./gitnexus-shared +RUN npm run build --prefix gitnexus-shared + +COPY gitnexus/package.json ./gitnexus/package.json +COPY gitnexus-web/package.json gitnexus-web/package-lock.json ./gitnexus-web/ +RUN npm ci --prefix gitnexus-web + +COPY gitnexus-web ./gitnexus-web +RUN npm run build --prefix gitnexus-web + +FROM nginx:1.27-alpine AS runtime + +COPY docker/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=builder /app/gitnexus-web/dist /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..d6ab86577 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,55 @@ +# Docker + +This folder contains the Docker assets for `gitnexus-web`. + +## Files + +- `Dockerfile` is the source for the published `gitnexus-web` image. It builds `gitnexus-shared` and `gitnexus-web` in a Node builder stage, then copies only the compiled static site into a tiny `nginx:alpine` runtime image. +- `nginx.conf` serves the built assets and falls back to `index.html` so client-side routes keep working. +- `compose.yaml` starts the published image with Docker Compose. +- `.env.example` is an optional shell env file for the example commands below. It sets the image name, container name, and exposed port. It does not inject application config into the frontend bundle. + +## Optional env file + +Create a local env file if you want reusable image/container names and host port settings: + +```bash +cp docker/.env.example docker/.env +set -a +source docker/.env +set +a +``` + +`docker/.env` is ignored by git via the repository-wide `.env` rules. + +## Run with Docker + +```bash +docker run --rm \ + --name "${CONTAINER_NAME:-gitnexus-web}" \ + -p "${HOST_PORT:-8080}:80" \ + "${IMAGE_NAME:-ghcr.io/abhigyanpatwari/gitnexus-web:latest}" +``` + +Then open `http://localhost:${HOST_PORT:-8080}`. + +## Run with Docker Compose + +```bash +docker compose --env-file docker/.env -f docker/compose.yaml up -d +``` + +If you do not want an env file, the defaults in `compose.yaml` are already set to: + +```text +image: ghcr.io/abhigyanpatwari/gitnexus-web:latest +container_name: gitnexus-web +port: 8080 -> 80 +``` + +## Notes + +- The published image serves the production frontend only. It does not start `gitnexus serve`. +- In backend mode, the app still defaults to `http://localhost:4747` unless you change the server URL in the UI. +- The Dockerfile is kept here so the published image definition is versioned with the app, even if most users only need `docker run` or `docker compose`. +- For iterative frontend development, `cd gitnexus-web && npm run dev` is still the faster workflow. diff --git a/docker/compose.yaml b/docker/compose.yaml new file mode 100644 index 000000000..ec3f1c9f2 --- /dev/null +++ b/docker/compose.yaml @@ -0,0 +1,7 @@ +services: + gitnexus-web: + image: ${IMAGE_NAME:-ghcr.io/abhigyanpatwari/gitnexus-web:latest} + container_name: ${CONTAINER_NAME:-gitnexus-web} + ports: + - "${HOST_PORT:-8080}:80" + restart: unless-stopped diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 000000000..0ee6c38a4 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,16 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} diff --git a/gitnexus-web/package-lock.json b/gitnexus-web/package-lock.json index 2190e27a6..845fe1998 100644 --- a/gitnexus-web/package-lock.json +++ b/gitnexus-web/package-lock.json @@ -2224,257 +2224,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@swc/core": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.8.tgz", - "integrity": "sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.25" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.8", - "@swc/core-darwin-x64": "1.15.8", - "@swc/core-linux-arm-gnueabihf": "1.15.8", - "@swc/core-linux-arm64-gnu": "1.15.8", - "@swc/core-linux-arm64-musl": "1.15.8", - "@swc/core-linux-x64-gnu": "1.15.8", - "@swc/core-linux-x64-musl": "1.15.8", - "@swc/core-win32-arm64-msvc": "1.15.8", - "@swc/core-win32-ia32-msvc": "1.15.8", - "@swc/core-win32-x64-msvc": "1.15.8" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.8.tgz", - "integrity": "sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.8.tgz", - "integrity": "sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.8.tgz", - "integrity": "sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.8.tgz", - "integrity": "sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.8.tgz", - "integrity": "sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.8.tgz", - "integrity": "sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.8.tgz", - "integrity": "sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.8.tgz", - "integrity": "sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.8.tgz", - "integrity": "sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.8.tgz", - "integrity": "sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true - }, - "node_modules/@swc/types": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", - "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, - "node_modules/@swc/wasm": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@swc/wasm/-/wasm-1.15.8.tgz", - "integrity": "sha512-RG2BxGbbsjtddFCo1ghKH6A/BMXbY1eMBfpysV0lJMCpI4DZOjW1BNBnxvBt7YsYmlJtmy5UXIg9/4ekBTFFaQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true - }, "node_modules/@tailwindcss/node": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", diff --git a/gitnexus-web/vite.config.ts b/gitnexus-web/vite.config.ts index b177f6804..a62b9e586 100644 --- a/gitnexus-web/vite.config.ts +++ b/gitnexus-web/vite.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ alias: { '@': path.resolve(__dirname, './src'), '@shared': path.resolve(__dirname, '../shared'), + 'gitnexus-shared': path.resolve(__dirname, '../gitnexus-shared/src/index.ts'), // Fix for Rollup failing to resolve this deep import from @langchain/anthropic '@anthropic-ai/sdk/lib/transform-json-schema': path.resolve( __dirname,