diff --git a/ts/.dockerignore b/ts/.dockerignore new file mode 100644 index 00000000..aa9d1653 --- /dev/null +++ b/ts/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.git +deploy +**/dist +**/.turbo +*.log +.env* diff --git a/ts/Containerfile b/ts/Containerfile new file mode 100644 index 00000000..3b65971a --- /dev/null +++ b/ts/Containerfile @@ -0,0 +1,44 @@ +# TrustGraph TypeScript — multi-stage build for all Node.js services. +# A single image is built once; each service overrides CMD to pick its entrypoint. + +# --------------------------------------------------------------------------- +# Stage 1: Build +# --------------------------------------------------------------------------- +FROM node:22-slim AS builder +RUN corepack enable && corepack prepare pnpm@9.15.0 --activate +WORKDIR /app + +# Copy workspace config first for layer caching +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.base.json ./ +COPY packages/base/package.json packages/base/tsconfig.json packages/base/ +COPY packages/client/package.json packages/client/tsconfig.json packages/client/ +COPY packages/flow/package.json packages/flow/tsconfig.json packages/flow/ +COPY packages/cli/package.json packages/cli/tsconfig.json packages/cli/ +COPY packages/mcp/package.json packages/mcp/tsconfig.json packages/mcp/ + +RUN pnpm install --frozen-lockfile + +# Copy source and build +COPY packages/ packages/ +COPY tsconfig.json ./ +RUN pnpm build + +# --------------------------------------------------------------------------- +# Stage 2: Runtime +# --------------------------------------------------------------------------- +FROM node:22-slim AS runtime +WORKDIR /app + +# Copy built output and production deps +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/packages ./packages +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/pnpm-workspace.yaml ./ +COPY entrypoints/ ./entrypoints/ + +# Default env +ENV NODE_ENV=production +ENV NATS_URL=nats://nats:4222 + +EXPOSE 8088 +CMD ["node", "entrypoints/gateway.mjs"] diff --git a/ts/deploy/docker-compose.yml b/ts/deploy/docker-compose.yml index d01cca7b..a41b7991 100644 --- a/ts/deploy/docker-compose.yml +++ b/ts/deploy/docker-compose.yml @@ -197,82 +197,89 @@ services: restart: unless-stopped # --------------------------------------------------------------------------- - # TrustGraph Services (placeholders — will be filled in later) + # TrustGraph Application Services # --------------------------------------------------------------------------- - # - # gateway: - # build: - # context: ../ - # dockerfile: packages/base/Dockerfile - # target: gateway - # ports: - # - "${GATEWAY_PORT:-8088}:8000" - # environment: - # - NATS_URL=nats://nats:4222 - # - FALKORDB_URL=redis://falkordb:6379 - # - QDRANT_URL=http://qdrant:6333 - # - OPENAI_TOKEN=${OPENAI_TOKEN} - # - CLAUDE_KEY=${CLAUDE_KEY} - # - GATEWAY_SECRET=${GATEWAY_SECRET} - # - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 - # - OTEL_SERVICE_NAME=gateway - # depends_on: - # nats: - # condition: service_healthy - # falkordb: - # condition: service_healthy - # qdrant: - # condition: service_healthy - # networks: - # - trustgraph - # - # text-completion: - # build: - # context: ../ - # dockerfile: packages/base/Dockerfile - # target: text-completion - # environment: - # - NATS_URL=nats://nats:4222 - # - OPENAI_TOKEN=${OPENAI_TOKEN} - # - CLAUDE_KEY=${CLAUDE_KEY} - # - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 - # - OTEL_SERVICE_NAME=text-completion - # depends_on: - # nats: - # condition: service_healthy - # networks: - # - trustgraph - # - # graph-rag: - # build: - # context: ../ - # dockerfile: packages/base/Dockerfile - # target: graph-rag - # environment: - # - NATS_URL=nats://nats:4222 - # - FALKORDB_URL=redis://falkordb:6379 - # - QDRANT_URL=http://qdrant:6333 - # - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 - # - OTEL_SERVICE_NAME=graph-rag - # depends_on: - # nats: - # condition: service_healthy - # falkordb: - # condition: service_healthy - # qdrant: - # condition: service_healthy - # networks: - # - trustgraph - # - # workbench: - # build: - # context: ../ - # dockerfile: packages/workbench/Dockerfile - # ports: - # - "3001:3000" - # environment: - # - GATEWAY_URL=http://gateway:8000 - # depends_on: - # - gateway - # networks: - # - trustgraph + + gateway: + image: trustgraph-ts:local + build: + context: ../ + dockerfile: Containerfile + command: ["node", "entrypoints/gateway.mjs"] + ports: + - "${GATEWAY_PORT:-8088}:8088" + environment: + - NATS_URL=nats://nats:4222 + - GATEWAY_PORT=8088 + - GATEWAY_SECRET=${GATEWAY_SECRET:-} + depends_on: + nats: + condition: service_healthy + networks: + - trustgraph + restart: unless-stopped + + config-service: + image: trustgraph-ts:local + command: ["node", "entrypoints/config.mjs"] + environment: + - NATS_URL=nats://nats:4222 + depends_on: + nats: + condition: service_healthy + networks: + - trustgraph + restart: unless-stopped + + text-completion: + image: trustgraph-ts:local + command: ["node", "entrypoints/text-completion-openai.mjs"] + environment: + - NATS_URL=nats://nats:4222 + - OPENAI_TOKEN=${OPENAI_TOKEN:-} + - OPENAI_BASE_URL=${OPENAI_BASE_URL:-} + depends_on: + nats: + condition: service_healthy + networks: + - trustgraph + restart: unless-stopped + + prompt: + image: trustgraph-ts:local + command: ["node", "entrypoints/prompt.mjs"] + environment: + - NATS_URL=nats://nats:4222 + depends_on: + nats: + condition: service_healthy + networks: + - trustgraph + restart: unless-stopped + + embeddings: + image: trustgraph-ts:local + command: ["node", "entrypoints/embeddings.mjs"] + environment: + - NATS_URL=nats://nats:4222 + - OLLAMA_URL=http://ollama:11434 + depends_on: + nats: + condition: service_healthy + ollama: + condition: service_started + networks: + - trustgraph + restart: unless-stopped + + workbench: + build: + context: ../ + dockerfile: packages/workbench/Containerfile + ports: + - "${WORKBENCH_PORT:-3001}:80" + depends_on: + - gateway + networks: + - trustgraph + restart: unless-stopped diff --git a/ts/entrypoints/agent.mjs b/ts/entrypoints/agent.mjs new file mode 100644 index 00000000..855b8942 --- /dev/null +++ b/ts/entrypoints/agent.mjs @@ -0,0 +1,7 @@ +// Will work once the agent service is merged. +import("../packages/flow/dist/agent/react/service.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/config.mjs b/ts/entrypoints/config.mjs new file mode 100644 index 00000000..4b1f79f4 --- /dev/null +++ b/ts/entrypoints/config.mjs @@ -0,0 +1,6 @@ +import("../packages/flow/dist/config/service.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/embeddings.mjs b/ts/entrypoints/embeddings.mjs new file mode 100644 index 00000000..fdcf25fe --- /dev/null +++ b/ts/entrypoints/embeddings.mjs @@ -0,0 +1,6 @@ +import("../packages/flow/dist/embeddings/ollama.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/gateway.mjs b/ts/entrypoints/gateway.mjs new file mode 100644 index 00000000..796ab165 --- /dev/null +++ b/ts/entrypoints/gateway.mjs @@ -0,0 +1,6 @@ +import("../packages/flow/dist/gateway/server.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/librarian.mjs b/ts/entrypoints/librarian.mjs new file mode 100644 index 00000000..3dd466fd --- /dev/null +++ b/ts/entrypoints/librarian.mjs @@ -0,0 +1,7 @@ +// Will work once the librarian service is merged. +import("../packages/flow/dist/librarian/service.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/prompt.mjs b/ts/entrypoints/prompt.mjs new file mode 100644 index 00000000..4f9984c7 --- /dev/null +++ b/ts/entrypoints/prompt.mjs @@ -0,0 +1,6 @@ +import("../packages/flow/dist/prompt/template.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/text-completion-claude.mjs b/ts/entrypoints/text-completion-claude.mjs new file mode 100644 index 00000000..6f5bec18 --- /dev/null +++ b/ts/entrypoints/text-completion-claude.mjs @@ -0,0 +1,6 @@ +import("../packages/flow/dist/model/text-completion/claude.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/entrypoints/text-completion-openai.mjs b/ts/entrypoints/text-completion-openai.mjs new file mode 100644 index 00000000..04360884 --- /dev/null +++ b/ts/entrypoints/text-completion-openai.mjs @@ -0,0 +1,6 @@ +import("../packages/flow/dist/model/text-completion/openai.js") + .then((m) => m.run()) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/ts/packages/workbench/Containerfile b/ts/packages/workbench/Containerfile new file mode 100644 index 00000000..b2d3aef7 --- /dev/null +++ b/ts/packages/workbench/Containerfile @@ -0,0 +1,27 @@ +# TrustGraph Workbench — Vite SPA served by nginx. + +# --------------------------------------------------------------------------- +# Stage 1: Build +# --------------------------------------------------------------------------- +FROM node:22-slim AS builder +RUN corepack enable && corepack prepare pnpm@9.15.0 --activate +WORKDIR /app + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.base.json ./ +COPY packages/base/package.json packages/base/tsconfig.json packages/base/ +COPY packages/client/package.json packages/client/tsconfig.json packages/client/ +COPY packages/workbench/package.json packages/workbench/tsconfig.json packages/workbench/vite.config.ts packages/workbench/ + +RUN pnpm install --frozen-lockfile + +COPY packages/ packages/ +COPY tsconfig.json ./ +RUN pnpm build --filter=@trustgraph/workbench + +# --------------------------------------------------------------------------- +# Stage 2: Serve +# --------------------------------------------------------------------------- +FROM nginx:alpine +COPY --from=builder /app/packages/workbench/dist /usr/share/nginx/html +COPY packages/workbench/nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 diff --git a/ts/packages/workbench/nginx.conf b/ts/packages/workbench/nginx.conf new file mode 100644 index 00000000..7629f1fe --- /dev/null +++ b/ts/packages/workbench/nginx.conf @@ -0,0 +1,28 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # SPA routing + location / { + try_files $uri $uri/ /index.html; + } + + # API proxy to gateway + location /api/v1/ { + proxy_pass http://gateway:8088; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # WebSocket proxy + location /api/v1/socket { + proxy_pass http://gateway:8088; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_read_timeout 86400; + } +}